1 | #ifndef ENGINE_CLIENT_VIDEO_H |
2 | #define ENGINE_CLIENT_VIDEO_H |
3 | |
4 | #include <base/lock.h> |
5 | |
6 | extern "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 | |
19 | class IGraphics; |
20 | class ISound; |
21 | class IStorage; |
22 | |
23 | extern CLock g_WriteLock; |
24 | |
25 | // a wrapper around a single output AVStream |
26 | class COutputStream |
27 | { |
28 | public: |
29 | AVStream *m_pStream = nullptr; |
30 | AVCodecContext *m_pCodecContext = nullptr; |
31 | |
32 | /* pts of the next frame that will be generated */ |
33 | int64_t m_SamplesCount = 0; |
34 | int64_t m_SamplesFrameCount = 0; |
35 | |
36 | std::vector<AVFrame *> m_vpFrames; |
37 | std::vector<AVFrame *> m_vpTmpFrames; |
38 | |
39 | std::vector<struct SwsContext *> m_vpSwsContexts; |
40 | std::vector<struct SwrContext *> m_vpSwrContexts; |
41 | }; |
42 | |
43 | class CVideo : public IVideo |
44 | { |
45 | public: |
46 | CVideo(IGraphics *pGraphics, ISound *pSound, IStorage *pStorage, int Width, int Height, const char *pName); |
47 | ~CVideo(); |
48 | |
49 | bool Start() override REQUIRES(!g_WriteLock); |
50 | void Stop() override; |
51 | void Pause(bool Pause) override; |
52 | bool IsRecording() override { return m_Recording; } |
53 | |
54 | void NextVideoFrame() override; |
55 | void NextVideoFrameThread() override; |
56 | |
57 | void NextAudioFrame(ISoundMixFunc Mix) override; |
58 | void NextAudioFrameTimeline(ISoundMixFunc Mix) override; |
59 | |
60 | static IVideo *Current() { return IVideo::ms_pCurrentVideo; } |
61 | |
62 | static void Init(); |
63 | |
64 | private: |
65 | void RunVideoThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!g_WriteLock); |
66 | void FillVideoFrame(size_t ThreadIndex) REQUIRES(!g_WriteLock); |
67 | void UpdateVideoBufferFromGraphics(size_t ThreadIndex); |
68 | |
69 | void RunAudioThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!g_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(g_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 | |
95 | bool m_Started; |
96 | bool m_Stopped; |
97 | bool m_Recording; |
98 | |
99 | size_t m_VideoThreads = 2; |
100 | size_t m_CurVideoThreadIndex = 0; |
101 | size_t m_AudioThreads = 2; |
102 | size_t m_CurAudioThreadIndex = 0; |
103 | |
104 | class CVideoRecorderThread |
105 | { |
106 | public: |
107 | std::thread m_Thread; |
108 | std::mutex m_Mutex; |
109 | std::condition_variable m_Cond; |
110 | |
111 | bool m_Started = false; |
112 | bool m_Finished = false; |
113 | bool m_HasVideoFrame = false; |
114 | |
115 | std::mutex m_VideoFillMutex; |
116 | std::condition_variable m_VideoFillCond; |
117 | uint64_t m_VideoFrameToFill = 0; |
118 | }; |
119 | |
120 | std::vector<std::unique_ptr<CVideoRecorderThread>> m_vpVideoThreads; |
121 | |
122 | class CAudioRecorderThread |
123 | { |
124 | public: |
125 | std::thread m_Thread; |
126 | std::mutex m_Mutex; |
127 | std::condition_variable m_Cond; |
128 | |
129 | bool m_Started = false; |
130 | bool m_Finished = false; |
131 | bool m_HasAudioFrame = false; |
132 | |
133 | std::mutex m_AudioFillMutex; |
134 | std::condition_variable m_AudioFillCond; |
135 | uint64_t m_AudioFrameToFill = 0; |
136 | int64_t m_SampleCountStart = 0; |
137 | }; |
138 | |
139 | std::vector<std::unique_ptr<CAudioRecorderThread>> m_vpAudioThreads; |
140 | |
141 | std::atomic<int32_t> m_ProcessingVideoFrame; |
142 | std::atomic<int32_t> m_ProcessingAudioFrame; |
143 | |
144 | bool m_HasAudio; |
145 | |
146 | class CVideoBuffer |
147 | { |
148 | public: |
149 | std::vector<uint8_t> m_vBuffer; |
150 | }; |
151 | std::vector<CVideoBuffer> m_vVideoBuffers; |
152 | class CAudioBuffer |
153 | { |
154 | public: |
155 | int16_t m_aBuffer[4096]; |
156 | }; |
157 | std::vector<CAudioBuffer> m_vAudioBuffers; |
158 | |
159 | COutputStream m_VideoStream; |
160 | COutputStream m_AudioStream; |
161 | |
162 | const AVCodec *m_pVideoCodec; |
163 | const AVCodec *m_pAudioCodec; |
164 | |
165 | AVDictionary *m_pOptDict; |
166 | |
167 | AVFormatContext *m_pFormatContext; |
168 | const AVOutputFormat *m_pFormat; |
169 | }; |
170 | |
171 | #endif |
172 | |