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 | #define ALEN 2048 |
19 | |
20 | class CGraphics_Threaded; |
21 | class ISound; |
22 | class IStorage; |
23 | |
24 | extern CLock g_WriteLock; |
25 | |
26 | // a wrapper around a single output AVStream |
27 | struct OutputStream |
28 | { |
29 | AVStream *pSt = nullptr; |
30 | AVCodecContext *pEnc = nullptr; |
31 | |
32 | /* pts of the next frame that will be generated */ |
33 | int64_t NextPts = 0; |
34 | int64_t m_SamplesCount = 0; |
35 | int64_t m_SamplesFrameCount = 0; |
36 | |
37 | std::vector<AVFrame *> m_vpFrames; |
38 | std::vector<AVFrame *> m_vpTmpFrames; |
39 | |
40 | std::vector<struct SwsContext *> m_vpSwsCtxs; |
41 | std::vector<struct SwrContext *> m_vpSwrCtxs; |
42 | }; |
43 | |
44 | class CVideo : public IVideo |
45 | { |
46 | public: |
47 | CVideo(CGraphics_Threaded *pGraphics, ISound *pSound, IStorage *pStorage, int Width, int Height, const char *pName); |
48 | ~CVideo(); |
49 | |
50 | void Start() override REQUIRES(!g_WriteLock); |
51 | void Stop() override; |
52 | void Pause(bool Pause) override; |
53 | bool IsRecording() override { return m_Recording; } |
54 | |
55 | void NextVideoFrame() override; |
56 | void NextVideoFrameThread() override; |
57 | |
58 | void NextAudioFrame(ISoundMixFunc Mix) override; |
59 | void NextAudioFrameTimeline(ISoundMixFunc Mix) override; |
60 | |
61 | static IVideo *Current() { return IVideo::ms_pCurrentVideo; } |
62 | |
63 | static void Init() { av_log_set_level(AV_LOG_DEBUG); } |
64 | |
65 | private: |
66 | void RunVideoThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!g_WriteLock); |
67 | void FillVideoFrame(size_t ThreadIndex) REQUIRES(!g_WriteLock); |
68 | void ReadRGBFromGL(size_t ThreadIndex); |
69 | |
70 | void RunAudioThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!g_WriteLock); |
71 | void FillAudioFrame(size_t ThreadIndex); |
72 | |
73 | bool OpenVideo(); |
74 | bool OpenAudio(); |
75 | AVFrame *AllocPicture(enum AVPixelFormat PixFmt, int Width, int Height); |
76 | AVFrame *AllocAudioFrame(enum AVSampleFormat SampleFmt, uint64_t ChannelLayout, int SampleRate, int NbSamples); |
77 | |
78 | void WriteFrame(OutputStream *pStream, size_t ThreadIndex) REQUIRES(g_WriteLock); |
79 | void FinishFrames(OutputStream *pStream); |
80 | void CloseStream(OutputStream *pStream); |
81 | |
82 | bool AddStream(OutputStream *pStream, AVFormatContext *pOC, const AVCodec **ppCodec, enum AVCodecID CodecId) const; |
83 | |
84 | CGraphics_Threaded *m_pGraphics; |
85 | IStorage *m_pStorage; |
86 | ISound *m_pSound; |
87 | |
88 | int m_Width; |
89 | int m_Height; |
90 | char m_aName[256]; |
91 | //FILE *m_dbgfile; |
92 | uint64_t m_VSeq = 0; |
93 | uint64_t m_ASeq = 0; |
94 | uint64_t m_Vframe; |
95 | |
96 | int m_FPS; |
97 | |
98 | bool m_Started; |
99 | bool m_Recording; |
100 | |
101 | size_t m_VideoThreads = 2; |
102 | size_t m_CurVideoThreadIndex = 0; |
103 | size_t m_AudioThreads = 2; |
104 | size_t m_CurAudioThreadIndex = 0; |
105 | |
106 | struct SVideoRecorderThread |
107 | { |
108 | std::thread m_Thread; |
109 | std::mutex m_Mutex; |
110 | std::condition_variable m_Cond; |
111 | |
112 | bool m_Started = false; |
113 | bool m_Finished = false; |
114 | bool m_HasVideoFrame = false; |
115 | |
116 | std::mutex m_VideoFillMutex; |
117 | std::condition_variable m_VideoFillCond; |
118 | uint64_t m_VideoFrameToFill = 0; |
119 | }; |
120 | |
121 | std::vector<std::unique_ptr<SVideoRecorderThread>> m_vVideoThreads; |
122 | |
123 | struct SAudioRecorderThread |
124 | { |
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<SAudioRecorderThread>> m_vAudioThreads; |
140 | |
141 | std::atomic<int32_t> m_ProcessingVideoFrame; |
142 | std::atomic<int32_t> m_ProcessingAudioFrame; |
143 | |
144 | bool m_HasAudio; |
145 | |
146 | struct SVideoSoundBuffer |
147 | { |
148 | int16_t m_aBuffer[ALEN * 2]; |
149 | }; |
150 | std::vector<SVideoSoundBuffer> m_vBuffer; |
151 | std::vector<std::vector<uint8_t>> m_vPixelHelper; |
152 | |
153 | OutputStream m_VideoStream; |
154 | OutputStream m_AudioStream; |
155 | |
156 | const AVCodec *m_pVideoCodec; |
157 | const AVCodec *m_pAudioCodec; |
158 | |
159 | AVDictionary *m_pOptDict; |
160 | |
161 | AVFormatContext *m_pFormatContext; |
162 | const AVOutputFormat *m_pFormat; |
163 | }; |
164 | |
165 | #endif |
166 | |