1 /*
2 Copyright (c) 2012-2020 Maarten Baert <maarten-baert@hotmail.com>
3 
4 This file is part of SimpleScreenRecorder.
5 
6 SimpleScreenRecorder is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 SimpleScreenRecorder is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with SimpleScreenRecorder.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "SSRVideoStreamReader.h"
21 
22 #include "Logger.h"
23 
24 #include <fcntl.h>
25 #include <sys/file.h>
26 #include <sys/mman.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 
SSRVideoStreamReader(const std::string & channel,const SSRVideoStream & stream)30 SSRVideoStreamReader::SSRVideoStreamReader(const std::string& channel, const SSRVideoStream& stream) {
31 
32 	m_stream = stream;
33 	m_channel_directory = "/dev/shm/ssr-" + ((channel.empty())? "channel-" + GetUserName() : channel);
34 	m_filename_main = m_channel_directory + "/video-" + stream.m_stream_name;
35 	m_page_size = sysconf(_SC_PAGE_SIZE);
36 
37 	m_fd_main = -1;
38 	m_mmap_ptr_main = MAP_FAILED;
39 	m_mmap_size_main = 0;
40 
41 	for(unsigned int i = 0; i < GLINJECT_RING_BUFFER_SIZE; ++i) {
42 		FrameData &fd = m_frame_data[i];
43 		fd.m_filename_frame = m_channel_directory + "/videoframe" + NumToString(i) + "-" + stream.m_stream_name;
44 		fd.m_fd_frame = -1;
45 		fd.m_mmap_ptr_frame = MAP_FAILED;
46 		fd.m_mmap_size_frame = 0;
47 	}
48 
49 	try {
50 		Init();
51 	} catch(...) {
52 		Free();
53 		throw;
54 	}
55 
56 }
57 
~SSRVideoStreamReader()58 SSRVideoStreamReader::~SSRVideoStreamReader() {
59 	Free();
60 }
61 
Init()62 void SSRVideoStreamReader::Init() {
63 
64 	Logger::LogInfo("[SSRVideoStreamReader::Init] " + Logger::tr("Created video stream reader."));
65 
66 	// open main file
67 	m_fd_main = open(m_filename_main.c_str(), O_RDWR | O_CLOEXEC);
68 	if(m_fd_main == -1) {
69 		Logger::LogError("[SSRVideoStreamReader::Init] " + Logger::tr("Error: Can't open video stream file!"));
70 		throw SSRStreamException();
71 	}
72 
73 	// resize main file
74 	m_mmap_size_main = (sizeof(GLInjectHeader) + GLINJECT_RING_BUFFER_SIZE * sizeof(GLInjectFrameInfo) + m_page_size - 1) / m_page_size * m_page_size;
75 	if(ftruncate(m_fd_main, m_mmap_size_main) == -1) {
76 		Logger::LogError("[SSRVideoStreamReader::Init] " + Logger::tr("Error: Can't resize video stream file!"));
77 		throw SSRStreamException();
78 	}
79 
80 	// map main file
81 	m_mmap_ptr_main = mmap(NULL, m_mmap_size_main, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd_main, 0);
82 	if(m_mmap_ptr_main == MAP_FAILED) {
83 		Logger::LogError("[SSRVideoStreamReader::Init] " + Logger::tr("Error: Can't memory-map video stream file!"));
84 		throw SSRStreamException();
85 	}
86 
87 	// open frame files
88 	for(unsigned int i = 0; i < GLINJECT_RING_BUFFER_SIZE; ++i) {
89 		FrameData &fd = m_frame_data[i];
90 		fd.m_fd_frame = open(fd.m_filename_frame.c_str(), O_RDWR | O_CLOEXEC);
91 		if(fd.m_fd_frame == -1) {
92 			Logger::LogError("[SSRVideoStreamReader::Init] " + Logger::tr("Error: Can't open video frame file!"));
93 			throw SSRStreamException();
94 		}
95 	}
96 
97 	// initialize header
98 	GLInjectHeader *header = GetGLInjectHeader();
99 	header->capture_flags = 0;
100 	header->capture_target_fps = 0;
101 	std::atomic_thread_fence(std::memory_order_release);
102 
103 	// initialize frame counter
104 	std::atomic_thread_fence(std::memory_order_acquire);
105 	m_fps_last_timestamp = hrt_time_micro();
106 	m_fps_last_counter = header->frame_counter;
107 	m_fps_current = 0.0;
108 
109 }
110 
Free()111 void SSRVideoStreamReader::Free() {
112 
113 	for(unsigned int i = 0; i < GLINJECT_RING_BUFFER_SIZE; ++i) {
114 		FrameData &fd = m_frame_data[i];
115 
116 		// unmap frame file
117 		if(fd.m_mmap_ptr_frame != MAP_FAILED) {
118 			munmap(fd.m_mmap_ptr_frame, fd.m_mmap_size_frame);
119 			fd.m_mmap_ptr_frame = MAP_FAILED;
120 		}
121 
122 		// close frame file
123 		if(fd.m_fd_frame != -1) {
124 			close(fd.m_fd_frame);
125 			fd.m_fd_frame = -1;
126 		}
127 
128 	}
129 
130 	// unmap main file
131 	if(m_mmap_ptr_main != MAP_FAILED) {
132 		munmap(m_mmap_ptr_main, m_mmap_size_main);
133 		m_mmap_ptr_main = MAP_FAILED;
134 	}
135 
136 	// close main file
137 	if(m_fd_main != -1) {
138 		close(m_fd_main);
139 		m_fd_main = -1;
140 	}
141 
142 	Logger::LogInfo("[SSRVideoStreamReader::Init] " + Logger::tr("Destroyed video stream reader."));
143 
144 }
145 
GetCurrentSize(unsigned int * width,unsigned int * height)146 void SSRVideoStreamReader::GetCurrentSize(unsigned int* width, unsigned int* height) {
147 	GLInjectHeader *header = GetGLInjectHeader();
148 	std::atomic_thread_fence(std::memory_order_acquire);
149 	if(header->identifier != GLINJECT_IDENTIFIER) {
150 		*width = 0;
151 		*height = 0;
152 		return;
153 	}
154 	std::atomic_thread_fence(std::memory_order_acquire);
155 	*width = header->current_width;
156 	*height = header->current_height;
157 }
158 
GetFPS()159 double SSRVideoStreamReader::GetFPS() {
160 	GLInjectHeader *header = GetGLInjectHeader();
161 	int64_t timestamp = hrt_time_micro();
162 	std::atomic_thread_fence(std::memory_order_acquire);
163 	uint32_t frame_counter = header->frame_counter;
164 	unsigned int time = timestamp - m_fps_last_timestamp;
165 	if(time > 500000) {
166 		unsigned int frames = frame_counter - m_fps_last_counter;
167 		m_fps_last_timestamp = timestamp;
168 		m_fps_last_counter = frame_counter;
169 		m_fps_current = (double) frames / ((double) time * 1.0e-6);
170 	}
171 	return m_fps_current;
172 }
173 
ChangeCaptureParameters(unsigned int flags,unsigned int target_fps)174 void SSRVideoStreamReader::ChangeCaptureParameters(unsigned int flags, unsigned int target_fps) {
175 	GLInjectHeader *header = GetGLInjectHeader();
176 	header->capture_flags = flags;
177 	header->capture_target_fps = target_fps;
178 	std::atomic_thread_fence(std::memory_order_release);
179 }
180 
Clear()181 void SSRVideoStreamReader::Clear() {
182 	GLInjectHeader *header = GetGLInjectHeader();
183 	std::atomic_thread_fence(std::memory_order_acquire);
184 	header->ring_buffer_read_pos = header->ring_buffer_write_pos;
185 	std::atomic_thread_fence(std::memory_order_release);
186 }
187 
GetFrame(int64_t * timestamp,unsigned int * width,unsigned int * height,int * stride)188 void* SSRVideoStreamReader::GetFrame(int64_t* timestamp, unsigned int* width, unsigned int* height, int* stride) {
189 
190 	// make sure that the stream has been initialized
191 	GLInjectHeader *header = GetGLInjectHeader();
192 	std::atomic_thread_fence(std::memory_order_acquire);
193 	if(header->identifier != GLINJECT_IDENTIFIER)
194 		return NULL;
195 
196 	// make sure that at least one frame is available
197 	std::atomic_thread_fence(std::memory_order_acquire);
198 	unsigned int read_pos = header->ring_buffer_read_pos;
199 	unsigned int write_pos = header->ring_buffer_write_pos;
200 	if(read_pos == write_pos)
201 		return NULL;
202 
203 	// read frame info
204 	GLInjectFrameInfo *frameinfo = GetGLInjectFrameInfo(read_pos % GLINJECT_RING_BUFFER_SIZE);
205 	std::atomic_thread_fence(std::memory_order_acquire);
206 	*timestamp = frameinfo->timestamp;
207 	*width = frameinfo->width;
208 	*height = frameinfo->height;
209 	*stride = frameinfo->stride;
210 
211 	// verify the size (should never happen unless someone is messing with the files)
212 	if(*width < 2 || *height < 2)
213 		return NULL;
214 	if(*width > SSR_MAX_IMAGE_SIZE || *height > SSR_MAX_IMAGE_SIZE)
215 		return NULL;
216 	if(abs(*stride) > SSR_MAX_IMAGE_SIZE * 4)
217 		return NULL;
218 
219 	// read frame
220 	FrameData &fd = m_frame_data[read_pos % GLINJECT_RING_BUFFER_SIZE];
221 	size_t required_size = (size_t) abs(*stride) * (size_t) *height;
222 	if(required_size > fd.m_mmap_size_frame) {
223 
224 		// calculate new size
225 		required_size = (required_size + m_page_size - 1) / m_page_size * m_page_size;
226 
227 		// unmap frame file
228 		if(fd.m_mmap_ptr_frame != MAP_FAILED) {
229 			munmap(fd.m_mmap_ptr_frame, fd.m_mmap_size_frame);
230 			fd.m_mmap_ptr_frame = MAP_FAILED;
231 			fd.m_mmap_size_frame = 0;
232 		}
233 
234 		// check frame file size
235 		{
236 			struct stat statinfo;
237 			if(fstat(fd.m_fd_frame, &statinfo) == -1 || (size_t) statinfo.st_size < required_size) {
238 				Logger::LogError("[SSRVideoStreamReader::GetFrame] " + Logger::tr("Error: Size of video frame file is incorrect!"));
239 				throw SSRStreamException();
240 			}
241 			required_size = statinfo.st_size / m_page_size * m_page_size;
242 		}
243 
244 		// map frame file
245 		fd.m_mmap_ptr_frame = mmap(NULL, required_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.m_fd_frame, 0);
246 		if(fd.m_mmap_ptr_frame == MAP_FAILED) {
247 			Logger::LogError("[SSRVideoStreamReader::GetFrame] " + Logger::tr("Error: Can't memory-map video frame file!"));
248 			throw SSRStreamException();
249 		}
250 		fd.m_mmap_size_frame = required_size;
251 
252 	}
253 
254 	return fd.m_mmap_ptr_frame;
255 }
256 
NextFrame()257 void SSRVideoStreamReader::NextFrame() {
258 
259 	// tell the compiler that it should read the entire frame before continuing
260 	// This is essentially a 'load-store' fence, which should be provided by both acquire and release fences,
261 	// but I'm not completely sure so let's use both just to be safe.
262 	std::atomic_thread_fence(std::memory_order_acq_rel);
263 
264 	// go to the next frame
265 	GLInjectHeader *header = GetGLInjectHeader();
266 	header->ring_buffer_read_pos = (header->ring_buffer_read_pos + 1) % (GLINJECT_RING_BUFFER_SIZE * 2);
267 	std::atomic_thread_fence(std::memory_order_release);
268 
269 }
270