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 419 : void setStreamCallback(unsigned stream, StreamCallback cb = {})
104 : {
105 419 : if (streams_.size() <= stream)
106 410 : streams_.resize(stream + 1);
107 419 : streams_[stream] = std::move(cb);
108 419 : }
109 :
110 91042348 : 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 411 : AVStream* getStream(unsigned stream)
117 : {
118 411 : if (stream >= inputCtx_->nb_streams) {
119 0 : JAMI_ERROR("Stream index is out of range: {}", stream);
120 0 : return {};
121 : }
122 411 : 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 : AVIOInterruptCB interruptCallback_ {};
138 : std::vector<StreamCallback> streams_;
139 : int64_t startTime_;
140 : int64_t lastReadPacketTime_ {};
141 : DeviceParams inputParams_;
142 : AVDictionary* options_ = nullptr;
143 : MediaDemuxer::CurrentState currentState_;
144 : std::mutex inputCtxMutex_ {};
145 : std::mutex audioBufferMutex_ {};
146 : std::mutex videoBufferMutex_ {};
147 : std::queue<std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>> videoBuffer_ {};
148 : std::queue<std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>> audioBuffer_ {};
149 : std::function<void()> needFrameCb_;
150 : std::function<void(bool)> fileFinishedCb_;
151 : std::function<void()> keyFrameRequestCb_;
152 : void clearFrames();
153 : bool pushFrameFrom(std::queue<std::unique_ptr<AVPacket, std::function<void(AVPacket*)>>>& buffer,
154 : bool isAudio,
155 : std::mutex& mutex);
156 : int baseWidth_ {};
157 : int baseHeight_ {};
158 : };
159 :
160 : class MediaDecoder
161 : {
162 : public:
163 : MediaDecoder();
164 : MediaDecoder(MediaObserver observer);
165 : MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, int index);
166 : MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, int index, MediaObserver observer);
167 : MediaDecoder(const std::shared_ptr<MediaDemuxer>& demuxer, AVMediaType type)
168 : : MediaDecoder(demuxer, demuxer->selectStream(type))
169 : {}
170 : ~MediaDecoder();
171 :
172 20 : void emulateRate() { emulateRate_ = true; }
173 :
174 : int openInput(const DeviceParams&);
175 : void setInterruptCallback(int (*cb)(void*), void* opaque);
176 : void setIOContext(MediaIOHandle* ioctx);
177 :
178 : int setup(AVMediaType type);
179 222 : int setupAudio() { return setup(AVMEDIA_TYPE_AUDIO); }
180 169 : int setupVideo() { return setup(AVMEDIA_TYPE_VIDEO); }
181 : void setKeyFrameRequestCb(std::function<void()> cb);
182 :
183 : MediaDemuxer::Status decode();
184 : DecodeStatus flush();
185 :
186 : int getWidth() const;
187 : int getHeight() const;
188 : std::string getDecoderName() const;
189 32 : bool isReady() const noexcept { return decoderReady_; }
190 :
191 : rational<double> getFps() const;
192 : AVPixelFormat getPixelFormat() const;
193 :
194 : void updateStartTime(int64_t startTime);
195 :
196 : bool emitFrame(bool isAudio);
197 : void flushBuffers();
198 : void setSeekTime(int64_t time);
199 : #ifdef ENABLE_HWACCEL
200 : void enableAccel(bool enableAccel);
201 : #endif
202 :
203 : MediaStream getStream(const std::string& name = "") const;
204 :
205 169 : void setResolutionChangedCallback(std::function<void(int, int)> cb) { resolutionChangedCallback_ = std::move(cb); }
206 :
207 221 : void setFEC(bool enable) { fecEnabled_ = enable; }
208 :
209 413 : void setContextCallback(const std::function<void()>& cb)
210 : {
211 413 : firstDecode_.exchange(true);
212 413 : contextCallback_ = cb;
213 413 : }
214 :
215 : private:
216 : NON_COPYABLE(MediaDecoder);
217 :
218 : DecodeStatus decode(AVPacket&);
219 :
220 : rational<unsigned> getTimeBase() const;
221 :
222 : std::shared_ptr<MediaDemuxer> demuxer_;
223 :
224 : const AVCodec* inputDecoder_ = nullptr;
225 : AVCodecContext* decoderCtx_ = nullptr;
226 : AVStream* avStream_ = nullptr;
227 : bool decoderReady_ = false;
228 : bool emulateRate_ = false;
229 : bool passthrough_ = false;
230 : int64_t startTime_;
231 : int64_t lastTimestamp_ {0};
232 :
233 : DeviceParams inputParams_;
234 :
235 : int correctPixFmt(int input_pix_fmt);
236 : int setupStream();
237 :
238 : bool fallback_ = false;
239 :
240 : #ifdef ENABLE_HWACCEL
241 : bool enableAccel_ = true;
242 : std::unique_ptr<video::HardwareAccel> accel_;
243 : unsigned short accelFailures_ = 0;
244 : #endif
245 : MediaObserver callback_;
246 : int prepareDecoderContext();
247 : int64_t seekTime_ = -1;
248 0 : void resetSeekTime() { seekTime_ = -1; }
249 : std::function<void(int, int)> resolutionChangedCallback_;
250 :
251 : int width_;
252 : int height_;
253 :
254 : bool fecEnabled_ {false};
255 :
256 : std::function<void()> contextCallback_;
257 : std::atomic_bool firstDecode_ {true};
258 :
259 : protected:
260 : AVDictionary* options_ = nullptr;
261 : };
262 :
263 : } // namespace jami
|