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 : #pragma once 18 : 19 : #ifdef HAVE_CONFIG_H 20 : #include "config.h" 21 : #endif 22 : 23 : #include "rational.h" 24 : 25 : #ifdef ENABLE_VIDEO 26 : #include "video/video_base.h" 27 : #endif // ENABLE_VIDEO 28 : 29 : #ifdef ENABLE_HWACCEL 30 : #include "video/accel.h" 31 : #endif 32 : #include "logger.h" 33 : 34 : #include "media_device.h" 35 : #include "media_stream.h" 36 : #include "media_buffer.h" 37 : #include "noncopyable.h" 38 : 39 : #include <asio/steady_timer.hpp> 40 : 41 : #include <string> 42 : #include <memory> 43 : #include <queue> 44 : 45 : extern "C" { 46 : struct AVCodecContext; 47 : struct AVStream; 48 : struct AVDictionary; 49 : struct AVFormatContext; 50 : struct AVCodec; 51 : enum AVMediaType; 52 : } 53 : 54 : namespace libjami { 55 : class AudioFrame; 56 : } 57 : 58 : namespace jami { 59 : 60 : using AudioFrame = libjami::AudioFrame; 61 : #ifdef ENABLE_VIDEO 62 : using VideoFrame = libjami::VideoFrame; 63 : #endif 64 : struct AudioFormat; 65 : class RingBuffer; 66 : class Resampler; 67 : class MediaIOHandle; 68 : class MediaDecoder; 69 : 70 : enum class DecodeStatus : uint8_t { 71 : Success, 72 : FrameFinished, 73 : EndOfFile, 74 : ReadError, 75 : DecodeError, 76 : RestartRequired, 77 : FallBack 78 : }; 79 : 80 : class MediaDemuxer : public std::enable_shared_from_this<MediaDemuxer> 81 : { 82 : public: 83 : MediaDemuxer(); 84 : ~MediaDemuxer(); 85 : 86 : enum class Status : uint8_t { Success, EndOfFile, ReadBufferOverflow, ReadError, FallBack, RestartRequired }; 87 : 88 : static const char* getStatusStr(Status status); 89 : 90 : enum class CurrentState : uint8_t { Demuxing, Finished }; 91 : using StreamCallback = std::function<DecodeStatus(AVPacket&)>; 92 : 93 : int openInput(const DeviceParams&); 94 : 95 : void setInterruptCallback(int (*cb)(void*), void* opaque); 96 : void setIOContext(MediaIOHandle* ioctx); 97 : 98 : void setKeyFrameRequestCb(std::function<void()> cb); 99 : 100 : void findStreamInfo(bool videoStream = false); 101 : int selectStream(AVMediaType type); 102 : 103 319 : void setStreamCallback(unsigned stream, StreamCallback cb = {}) 104 : { 105 319 : if (streams_.size() <= stream) 106 315 : streams_.resize(stream + 1); 107 320 : streams_[stream] = std::move(cb); 108 320 : } 109 : 110 15 : void updateCurrentState(MediaDemuxer::CurrentState state) { currentState_ = state; } 111 : 112 : void setFileFinishedCb(std::function<void(bool)> cb); 113 : 114 : MediaDemuxer::CurrentState getCurrentState() { return currentState_; } 115 : 116 320 : AVStream* getStream(unsigned stream) 117 : { 118 320 : if (stream >= inputCtx_->nb_streams) { 119 0 : JAMI_ERR("Stream index is out of range: %u", stream); 120 0 : return {}; 121 : } 122 320 : return inputCtx_->streams[stream]; 123 : } 124 : 125 : Status decode(); 126 : Status demuxe(); 127 : 128 : int64_t getDuration() const; 129 : bool seekFrame(int stream_index, int64_t timestamp); 130 : void setNeedFrameCb(std::function<void()> cb); 131 : bool emitFrame(bool isAudio); 132 : 133 : private: 134 : bool streamInfoFound_ {false}; 135 : std::unique_ptr<asio::steady_timer> streamInfoTimer_; 136 : AVFormatContext* inputCtx_ = nullptr; 137 : std::vector<StreamCallback> streams_; 138 : int64_t startTime_; 139 : int64_t lastReadPacketTime_ {}; 140 : DeviceParams inputParams_; 141 : AVDictionary* options_ = nullptr; 142 : MediaDemuxer::CurrentState currentState_; 143 : std::mutex inputCtxMutex_ {}; 144 : std::mutex audioBufferMutex_ {}; 145 : std::mutex videoBufferMutex_ {}; 146 : std::queue<std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>> videoBuffer_ {}; 147 : std::queue<std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>> audioBuffer_ {}; 148 : std::function<void()> needFrameCb_; 149 : std::function<void(bool)> fileFinishedCb_; 150 : std::function<void()> keyFrameRequestCb_; 151 : void clearFrames(); 152 : bool pushFrameFrom(std::queue<std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>>& buffer, 153 : bool isAudio, 154 : std::mutex& mutex); 155 : int baseWidth_ {}; 156 : int baseHeight_ {}; 157 : }; 158 : 159 : class MediaDecoder 160 : { 161 : public: 162 : MediaDecoder(); 163 : MediaDecoder(MediaObserver observer); 164 : MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, int index); 165 : MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, int index, MediaObserver observer); 166 : MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, AVMediaType type) 167 : : MediaDecoder(demuxer, demuxer->selectStream(type)) 168 : {} 169 : ~MediaDecoder(); 170 : 171 10 : void emulateRate() { emulateRate_ = true; } 172 : 173 : int openInput(const DeviceParams&); 174 : void setInterruptCallback(int (*cb)(void*), void* opaque); 175 : void setIOContext(MediaIOHandle* ioctx); 176 : 177 : int setup(AVMediaType type); 178 176 : int setupAudio() { return setup(AVMEDIA_TYPE_AUDIO); } 179 134 : int setupVideo() { return setup(AVMEDIA_TYPE_VIDEO); } 180 : void setKeyFrameRequestCb(std::function<void()> cb); 181 : 182 : MediaDemuxer::Status decode(); 183 : DecodeStatus flush(); 184 : 185 : int getWidth() const; 186 : int getHeight() const; 187 : std::string getDecoderName() const; 188 : 189 : rational<double> getFps() const; 190 : AVPixelFormat getPixelFormat() const; 191 : 192 : void updateStartTime(int64_t startTime); 193 : 194 : bool emitFrame(bool isAudio); 195 : void flushBuffers(); 196 : void setSeekTime(int64_t time); 197 : #ifdef ENABLE_HWACCEL 198 : void enableAccel(bool enableAccel); 199 : #endif 200 : 201 : MediaStream getStream(const std::string& name = "") const; 202 : 203 134 : void setResolutionChangedCallback(std::function<void(int, int)> cb) { resolutionChangedCallback_ = std::move(cb); } 204 : 205 175 : void setFEC(bool enable) { fecEnabled_ = enable; } 206 : 207 311 : void setContextCallback(const std::function<void()>& cb) 208 : { 209 311 : firstDecode_.exchange(true); 210 311 : contextCallback_ = cb; 211 311 : } 212 : 213 : private: 214 : NON_COPYABLE(MediaDecoder); 215 : 216 : DecodeStatus decode(AVPacket&); 217 : 218 : rational<unsigned> getTimeBase() const; 219 : 220 : std::shared_ptr<MediaDemuxer> demuxer_; 221 : 222 : const AVCodec* inputDecoder_ = nullptr; 223 : AVCodecContext* decoderCtx_ = nullptr; 224 : AVStream* avStream_ = nullptr; 225 : bool emulateRate_ = false; 226 : int64_t startTime_; 227 : int64_t lastTimestamp_ {0}; 228 : 229 : DeviceParams inputParams_; 230 : 231 : int correctPixFmt(int input_pix_fmt); 232 : int setupStream(); 233 : 234 : bool fallback_ = false; 235 : 236 : #ifdef ENABLE_HWACCEL 237 : bool enableAccel_ = true; 238 : std::unique_ptr<video::HardwareAccel> accel_; 239 : unsigned short accelFailures_ = 0; 240 : #endif 241 : MediaObserver callback_; 242 : int prepareDecoderContext(); 243 : int64_t seekTime_ = -1; 244 0 : void resetSeekTime() { seekTime_ = -1; } 245 : std::function<void(int, int)> resolutionChangedCallback_; 246 : 247 : int width_; 248 : int height_; 249 : 250 : bool fecEnabled_ {false}; 251 : 252 : std::function<void()> contextCallback_; 253 : std::atomic_bool firstDecode_ {true}; 254 : 255 : protected: 256 : AVDictionary* options_ = nullptr; 257 : }; 258 : 259 : } // namespace jami