1 /*  smplayer, GUI front-end for mplayer.
2     Copyright (C) 2006-2021 Ricardo Villalba <ricardo@smplayer.info>
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 2 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, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19 #include "connectionshm.h"
20 #include <QCoreApplication>
21 #include <QTimer>
22 #include <QDebug>
23 
24 #include <fcntl.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <stdint.h>
28 #include <errno.h>
29 
30 static struct header_t {
31 	uint32_t header_size;
32 	uint32_t video_buffer_size;
33 	uint32_t width;
34 	uint32_t height;
35 	uint32_t bytes;
36 	uint32_t stride[3];
37 	uint32_t planes;
38 	uint32_t format;
39 	uint32_t frame_count;
40 	uint32_t busy;
41 	float fps;
42 	// MPV
43 	int32_t rotate;
44 	int32_t colorspace;
45 	int32_t colorspace_levels;
46 	int32_t colorspace_primaries;
47 	int32_t colorspace_gamma;
48 	int32_t colorspace_light;
49 	float colorspace_sig_peak;
50 	int32_t chroma_location;
51 	uint32_t reserved[20];
52 } * header = 0;
53 
54 
ConnectionShm(VideoLayerRender * parent)55 ConnectionShm::ConnectionShm(VideoLayerRender * parent)
56 	: ConnectionBase(parent)
57 	, buffer_size(0)
58 	, image_data(0)
59 {
60 	buffer_name = QString("/smplayer-%1").arg(QCoreApplication::applicationPid());
61 
62 	render_timer = new QTimer(this);
63 	connect(render_timer, SIGNAL(timeout()), this, SLOT(render_slot()));
64 	render_timer->setInterval(1000 / 60);
65 
66 	connect_timer = new QTimer(this);
67 	connect(connect_timer, SIGNAL(timeout()), this, SLOT(start_connection()));
68 	connect_timer->setInterval(100);
69 }
70 
~ConnectionShm()71 ConnectionShm::~ConnectionShm() {
72 }
73 
start()74 void ConnectionShm::start() {
75 	qDebug("ConnectionShm::start");
76 	connect_timer->start();
77 }
78 
stop()79 void ConnectionShm::stop() {
80 	qDebug("ConnectionShm::stop");
81 	stop_connection();
82 }
83 
render_slot()84 void ConnectionShm::render_slot() {
85 	static uint32_t last_frame = 0;
86 	//qDebug("ConnectionShm::render_slot");
87 
88 	if (header == 0) return;
89 
90 	if (header->frame_count == last_frame) return;
91 
92 	//qDebug("ConnectionShm::render_slot: frame_count: %d", header->frame_count);
93 	//qDebug("ConnectionShm::render_slot: %d %d", header->width, header->height);
94 	//qDebug("ConnectionShm::render_slot: header size: %d busy: %d", header->header_size, header->busy);
95 	//qDebug("ConnectionShm::render_slot: %p %p", header, image_data);
96 
97 
98 	if (header->busy == 1) {
99 		//qDebug("ConnectionShm::render_slot: busy (f: %d)", header->frame_count);
100 		return;
101 	}
102 	//qDebug("ConnectionShm::render_slot: frame_count: %d", header->frame_count);
103 
104 
105 	/*
106 	uint32_t count = 0;
107 	while (header->busy == 1 && count < 1000000) { count++; }
108 	if (count != 0) {
109 		qDebug("ConnectionShm::render_slot: count: %d", count);
110 	}
111 	*/
112 
113 	video_window->render();
114 	last_frame = header->frame_count;
115 }
116 
stop_slot()117 void ConnectionShm::stop_slot() {
118 	qDebug("ConnectionShm::stop_slot");
119 }
120 
start_connection()121 void ConnectionShm::start_connection() {
122 	qDebug("ConnectionShm::start_connection");
123 
124 	shm_fd = shm_open(buffer_name.toLatin1().constData(), O_RDONLY, S_IRUSR);
125 
126 	if (shm_fd == -1) {
127 		qDebug("ConnectionShm::start_connection: shm_open failed");
128 		return;
129 	}
130 
131 	header = (header_t *) mmap(NULL, sizeof(header), PROT_READ, MAP_SHARED, shm_fd, 0);
132 
133 	if (header == MAP_FAILED) {
134 		qDebug("ConnectionShm::start_connection: mmap failed");
135 		return;
136 	}
137 
138 	qDebug("ConnectionShm::start_connection: bytes: %d stride: %d %d %d planes: %d", header->bytes, header->stride[0], header->stride[1], header->stride[2], header->planes);
139 	qDebug("ConnectionShm::start_connection: header size: %d videobuffer size: %d", header->header_size, header->video_buffer_size);
140 
141 	int image_width = header->width;
142 	int image_height = header->height;
143 	int image_bytes = header->bytes;
144 
145 	buffer_size =  header->header_size + header->video_buffer_size;
146 	qDebug("ConnectionShm::start_connection: %d %d %d %f", image_width, image_height, image_bytes, header->fps);
147 
148 	header = (header_t *) mmap(NULL, buffer_size, PROT_READ, MAP_SHARED, shm_fd, 0);
149 
150 	if (header == MAP_FAILED) {
151 		qDebug("ConnectionShm::start_connection: mmap failed");
152 		return;
153 	}
154 
155 	if (image_width == 0 || image_height == 0) {
156 		qDebug("ConnectionShm::start_connection: wrong image size");
157 		return;
158 	}
159 
160 	image_data = (unsigned char*) header + header->header_size;
161 	//qDebug("ConnectionShm::start_connection: header: %p image_data: %p", header, image_data);
162 
163 	// Convert formats from mpv
164 	uint32_t format = header->format;
165 	switch (format) {
166 		case ConnectionBase::MP_YUV420P: format = ConnectionBase::I420; break;
167 		case ConnectionBase::MP_UYVY422: format = ConnectionBase::UYVY; break;
168 		case ConnectionBase::MP_RGB24: format = ConnectionBase::RGB24; break;
169 	}
170 
171 	video_window->init(image_width, image_height, image_bytes, format, image_data);
172 	qDebug("ConnectionShm::start_connection: header_size: %d format: %s", header->header_size,
173            formatToString(format).toLatin1().constData());
174 
175 	connect_timer->stop();
176 
177 	int fps = header->fps;
178 	if (fps < 10) fps = 60;
179 	render_timer->setInterval(1000 / fps / 2);
180 	render_timer->start();
181 	qDebug("ConnectionShm::start_connection: timer interval: %d", render_timer->interval());
182 
183 	qDebug("ConnectionShm::start_connection: rotate: %d chroma_location: %d", header->rotate, header->chroma_location);
184 	qDebug("ConnectionShm::start_connection: colorspace: %d levels: %d primaries: %d gamma: %d light: %d sig_peak: %f",
185            header->colorspace, header->colorspace_levels, header->colorspace_primaries,
186            header->colorspace_gamma, header->colorspace_light, header->colorspace_sig_peak);
187 }
188 
stop_connection()189 void ConnectionShm::stop_connection() {
190 	render_timer->stop();
191 	connect_timer->stop();
192 
193 	if (header == 0) return;
194 
195 	// Destroy the shared buffer
196 	if (munmap(header, buffer_size) == -1) {
197 		qDebug("ConnectionShm::stop_connection: munmap failed");
198 	}
199 	header = 0;
200 	image_data = 0;
201 }
202 
203 #include "moc_connectionshm.cpp"
204