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 323 : void setStreamCallback(unsigned stream, StreamCallback cb = {})
104 : {
105 323 : if (streams_.size() <= stream)
106 319 : streams_.resize(stream + 1);
107 323 : streams_[stream] = std::move(cb);
108 323 : }
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 323 : AVStream* getStream(unsigned stream)
117 : {
118 323 : if (stream >= inputCtx_->nb_streams) {
119 0 : JAMI_ERROR("Stream index is out of range: {}", stream);
120 0 : return {};
121 : }
122 323 : 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 177 : int setupAudio() { return setup(AVMEDIA_TYPE_AUDIO); }
179 136 : 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 136 : void setResolutionChangedCallback(std::function<void(int, int)> cb) { resolutionChangedCallback_ = std::move(cb); }
204 :
205 175 : void setFEC(bool enable) { fecEnabled_ = enable; }
206 :
207 314 : void setContextCallback(const std::function<void()>& cb)
208 : {
209 314 : firstDecode_.exchange(true);
210 314 : contextCallback_ = cb;
211 314 : }
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 : bool passthrough_ = false;
227 : int64_t startTime_;
228 : int64_t lastTimestamp_ {0};
229 :
230 : DeviceParams inputParams_;
231 :
232 : int correctPixFmt(int input_pix_fmt);
233 : int setupStream();
234 :
235 : bool fallback_ = false;
236 :
237 : #ifdef ENABLE_HWACCEL
238 : bool enableAccel_ = true;
239 : std::unique_ptr<video::HardwareAccel> accel_;
240 : unsigned short accelFailures_ = 0;
241 : #endif
242 : MediaObserver callback_;
243 : int prepareDecoderContext();
244 : int64_t seekTime_ = -1;
245 0 : void resetSeekTime() { seekTime_ = -1; }
246 : std::function<void(int, int)> resolutionChangedCallback_;
247 :
248 : int width_;
249 : int height_;
250 :
251 : bool fecEnabled_ {false};
252 :
253 : std::function<void()> contextCallback_;
254 : std::atomic_bool firstDecode_ {true};
255 :
256 : protected:
257 : AVDictionary* options_ = nullptr;
258 : };
259 :
260 : } // namespace jami
|