1#ifndef ENGINE_CLIENT_VIDEO_H
2#define ENGINE_CLIENT_VIDEO_H
3
4#include <base/lock.h>
5
6extern "C" {
7#include <libavcodec/avcodec.h>
8#include <libavformat/avformat.h>
9};
10
11#include <engine/shared/video.h>
12
13#include <atomic>
14#include <condition_variable>
15#include <mutex>
16#include <thread>
17#include <vector>
18
19class IGraphics;
20class ISound;
21class IStorage;
22
23// a wrapper around a single output AVStream
24class COutputStream
25{
26public:
27 AVStream *m_pStream = nullptr;
28 AVCodecContext *m_pCodecContext = nullptr;
29
30 /* pts of the next frame that will be generated */
31 int64_t m_SamplesCount = 0;
32 int64_t m_SamplesFrameCount = 0;
33
34 std::vector<AVFrame *> m_vpFrames;
35 std::vector<AVFrame *> m_vpTmpFrames;
36
37 std::vector<struct SwsContext *> m_vpSwsContexts;
38 std::vector<struct SwrContext *> m_vpSwrContexts;
39};
40
41class CVideo : public IVideo
42{
43public:
44 CVideo(IGraphics *pGraphics, ISound *pSound, IStorage *pStorage, int Width, int Height, int64_t LocalStartTime, const char *pName);
45 ~CVideo() override;
46
47 bool Start() override REQUIRES(!m_WriteLock);
48 void Stop() override;
49 void Pause(bool Pause) override;
50 bool IsRecording() const override { return m_Recording; }
51
52 void NextVideoFrame() override;
53 void NextVideoFrameThread() override;
54
55 void NextAudioFrame(ISoundMixFunc Mix) override;
56 void NextAudioFrameTimeline(ISoundMixFunc Mix) override;
57
58 int64_t Time() const override { return m_Time; }
59 float LocalTime() const override { return m_LocalTime; }
60 void SetLocalStartTime(int64_t LocalStartTime) override { m_LocalStartTime = LocalStartTime; }
61
62 static void Init();
63
64private:
65 void RunVideoThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!m_WriteLock);
66 void FillVideoFrame(size_t ThreadIndex) REQUIRES(!m_WriteLock);
67 void UpdateVideoBufferFromGraphics(size_t ThreadIndex);
68
69 void RunAudioThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!m_WriteLock);
70 void FillAudioFrame(size_t ThreadIndex);
71
72 bool OpenVideo();
73 bool OpenAudio();
74 AVFrame *AllocPicture(enum AVPixelFormat PixFmt, int Width, int Height);
75 AVFrame *AllocAudioFrame(enum AVSampleFormat SampleFmt, uint64_t ChannelLayout, int SampleRate, int NbSamples);
76
77 void WriteFrame(COutputStream *pStream, size_t ThreadIndex) REQUIRES(m_WriteLock);
78 void FinishFrames(COutputStream *pStream);
79 void CloseStream(COutputStream *pStream);
80
81 bool AddStream(COutputStream *pStream, AVFormatContext *pFormatContext, const AVCodec **ppCodec, enum AVCodecID CodecId) const;
82
83 IGraphics *m_pGraphics;
84 IStorage *m_pStorage;
85 ISound *m_pSound;
86
87 int m_Width;
88 int m_Height;
89 char m_aName[256];
90 uint64_t m_VideoFrameIndex = 0;
91 uint64_t m_AudioFrameIndex = 0;
92
93 int m_FPS;
94 int64_t m_TickTime;
95 int64_t m_LocalStartTime;
96 float m_LocalTime;
97 int64_t m_Time;
98
99 bool m_Started;
100 bool m_Stopped;
101 bool m_Recording;
102
103 CLock m_WriteLock;
104 size_t m_VideoThreads = 2;
105 size_t m_CurVideoThreadIndex = 0;
106 size_t m_AudioThreads = 2;
107 size_t m_CurAudioThreadIndex = 0;
108
109 class CVideoRecorderThread
110 {
111 public:
112 std::thread m_Thread;
113 std::mutex m_Mutex;
114 std::condition_variable m_Cond;
115
116 bool m_Started = false;
117 bool m_Finished = false;
118 bool m_HasVideoFrame = false;
119
120 std::mutex m_VideoFillMutex;
121 std::condition_variable m_VideoFillCond;
122 uint64_t m_VideoFrameToFill = 0;
123 };
124
125 std::vector<std::unique_ptr<CVideoRecorderThread>> m_vpVideoThreads;
126
127 class CAudioRecorderThread
128 {
129 public:
130 std::thread m_Thread;
131 std::mutex m_Mutex;
132 std::condition_variable m_Cond;
133
134 bool m_Started = false;
135 bool m_Finished = false;
136 bool m_HasAudioFrame = false;
137
138 std::mutex m_AudioFillMutex;
139 std::condition_variable m_AudioFillCond;
140 uint64_t m_AudioFrameToFill = 0;
141 int64_t m_SampleCountStart = 0;
142 };
143
144 std::vector<std::unique_ptr<CAudioRecorderThread>> m_vpAudioThreads;
145
146 std::atomic<int32_t> m_ProcessingVideoFrame;
147 std::atomic<int32_t> m_ProcessingAudioFrame;
148
149 bool m_HasAudio;
150
151 class CVideoBuffer
152 {
153 public:
154 std::vector<uint8_t> m_vBuffer;
155 };
156 std::vector<CVideoBuffer> m_vVideoBuffers;
157 class CAudioBuffer
158 {
159 public:
160 int16_t m_aBuffer[4096];
161 };
162 std::vector<CAudioBuffer> m_vAudioBuffers;
163
164 COutputStream m_VideoStream;
165 COutputStream m_AudioStream;
166
167 const AVCodec *m_pVideoCodec;
168 const AVCodec *m_pAudioCodec;
169
170 AVDictionary *m_pOptDict;
171
172 AVFormatContext *m_pFormatContext;
173 const AVOutputFormat *m_pFormat;
174};
175
176#endif
177