1 /*****************************************************************************
2 #                                                                            #
3 #    uStreamer - Lightweight and fast MJPG-HTTP streamer.                    #
4 #                                                                            #
5 #    This source file based on code of MJPG-Streamer.                        #
6 #                                                                            #
7 #    Copyright (C) 2005-2006  Laurent Pinchart & Michel Xhaard               #
8 #    Copyright (C) 2006  Gabriel A. Devenyi                                  #
9 #    Copyright (C) 2007  Tom Stöveken                                        #
10 #    Copyright (C) 2018-2021  Maxim Devaev <mdevaev@gmail.com>               #
11 #                                                                            #
12 #    This program is free software: you can redistribute it and/or modify    #
13 #    it under the terms of the GNU General Public License as published by    #
14 #    the Free Software Foundation, either version 3 of the License, or       #
15 #    (at your option) any later version.                                     #
16 #                                                                            #
17 #    This program is distributed in the hope that it will be useful,         #
18 #    but WITHOUT ANY WARRANTY; without even the implied warranty of          #
19 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           #
20 #    GNU General Public License for more details.                            #
21 #                                                                            #
22 #    You should have received a copy of the GNU General Public License       #
23 #    along with this program.  If not, see <https://www.gnu.org/licenses/>.  #
24 #                                                                            #
25 *****************************************************************************/
26 
27 
28 #include "encoder.h"
29 
30 
31 typedef struct {
32 	struct	jpeg_destination_mgr mgr; // Default manager
33 	JOCTET	*buf; // Start of buffer
34 	frame_s	*frame;
35 } _jpeg_dest_manager_s;
36 
37 
38 static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame);
39 
40 static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const frame_s *frame);
41 static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const frame_s *frame);
42 static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const frame_s *frame);
43 static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame);
44 
45 static void _jpeg_init_destination(j_compress_ptr jpeg);
46 static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg);
47 static void _jpeg_term_destination(j_compress_ptr jpeg);
48 
49 
cpu_encoder_compress(const frame_s * src,frame_s * dest,unsigned quality)50 void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality) {
51 	// This function based on compress_image_to_jpeg() from mjpg-streamer
52 
53 	struct jpeg_compress_struct jpeg;
54 	struct jpeg_error_mgr jpeg_error;
55 
56 	jpeg.err = jpeg_std_error(&jpeg_error);
57 	jpeg_create_compress(&jpeg);
58 
59 	_jpeg_set_dest_frame(&jpeg, dest);
60 
61 	jpeg.image_width = src->width;
62 	jpeg.image_height = src->height;
63 	jpeg.input_components = 3;
64 	jpeg.in_color_space = JCS_RGB;
65 
66 	jpeg_set_defaults(&jpeg);
67 	jpeg_set_quality(&jpeg, quality, TRUE);
68 
69 	jpeg_start_compress(&jpeg, TRUE);
70 
71 #	define WRITE_SCANLINES(_format, _func) \
72 		case _format: { _func(&jpeg, src); break; }
73 
74 	switch (src->format) {
75 		// https://www.fourcc.org/yuv.php
76 		WRITE_SCANLINES(V4L2_PIX_FMT_YUYV, _jpeg_write_scanlines_yuyv);
77 		WRITE_SCANLINES(V4L2_PIX_FMT_UYVY, _jpeg_write_scanlines_uyvy);
78 		WRITE_SCANLINES(V4L2_PIX_FMT_RGB565, _jpeg_write_scanlines_rgb565);
79 		WRITE_SCANLINES(V4L2_PIX_FMT_RGB24, _jpeg_write_scanlines_rgb24);
80 		default: assert(0 && "Unsupported input format for CPU encoder");
81 	}
82 
83 #	undef WRITE_SCANLINES
84 
85 	jpeg_finish_compress(&jpeg);
86 	jpeg_destroy_compress(&jpeg);
87 
88 	assert(dest->used > 0);
89 }
90 
_jpeg_set_dest_frame(j_compress_ptr jpeg,frame_s * frame)91 static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame) {
92 	if (jpeg->dest == NULL) {
93 		assert((jpeg->dest = (struct jpeg_destination_mgr *)(*jpeg->mem->alloc_small)(
94 			(j_common_ptr) jpeg, JPOOL_PERMANENT, sizeof(_jpeg_dest_manager_s)
95 		)));
96 	}
97 
98 	_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
99 	dest->mgr.init_destination = _jpeg_init_destination;
100 	dest->mgr.empty_output_buffer = _jpeg_empty_output_buffer;
101 	dest->mgr.term_destination = _jpeg_term_destination;
102 	dest->frame = frame;
103 
104 	frame->used = 0;
105 }
106 
107 #define YUV_R(_y, _, _v)	(((_y) + (359 * (_v))) >> 8)
108 #define YUV_G(_y, _u, _v)	(((_y) - (88 * (_u)) - (183 * (_v))) >> 8)
109 #define YUV_B(_y, _u, _)	(((_y) + (454 * (_u))) >> 8)
110 #define NORM_COMPONENT(_x)	(((_x) > 255) ? 255 : (((_x) < 0) ? 0 : (_x)))
111 
_jpeg_write_scanlines_yuyv(struct jpeg_compress_struct * jpeg,const frame_s * frame)112 static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
113 	uint8_t *line_buf;
114 	A_CALLOC(line_buf, frame->width * 3);
115 
116 	const unsigned padding = frame_get_padding(frame);
117 	const uint8_t *data = frame->data;
118 	unsigned z = 0;
119 
120 	while (jpeg->next_scanline < frame->height) {
121 		uint8_t *ptr = line_buf;
122 
123 		for (unsigned x = 0; x < frame->width; ++x) {
124 			int y = (!z ? data[0] << 8 : data[2] << 8);
125 			int u = data[1] - 128;
126 			int v = data[3] - 128;
127 
128 			int r = YUV_R(y, u, v);
129 			int g = YUV_G(y, u, v);
130 			int b = YUV_B(y, u, v);
131 
132 			*(ptr++) = NORM_COMPONENT(r);
133 			*(ptr++) = NORM_COMPONENT(g);
134 			*(ptr++) = NORM_COMPONENT(b);
135 
136 			if (z++) {
137 				z = 0;
138 				data += 4;
139 			}
140 		}
141 		data += padding;
142 
143 		JSAMPROW scanlines[1] = {line_buf};
144 		jpeg_write_scanlines(jpeg, scanlines, 1);
145 	}
146 
147 	free(line_buf);
148 }
149 
_jpeg_write_scanlines_uyvy(struct jpeg_compress_struct * jpeg,const frame_s * frame)150 static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
151 	uint8_t *line_buf;
152 	A_CALLOC(line_buf, frame->width * 3);
153 
154 	const unsigned padding = frame_get_padding(frame);
155 	const uint8_t *data = frame->data;
156 	unsigned z = 0;
157 
158 	while (jpeg->next_scanline < frame->height) {
159 		uint8_t *ptr = line_buf;
160 
161 		for (unsigned x = 0; x < frame->width; ++x) {
162 			int y = (!z ? data[1] << 8 : data[3] << 8);
163 			int u = data[0] - 128;
164 			int v = data[2] - 128;
165 
166 			int r = YUV_R(y, u, v);
167 			int g = YUV_G(y, u, v);
168 			int b = YUV_B(y, u, v);
169 
170 			*(ptr++) = NORM_COMPONENT(r);
171 			*(ptr++) = NORM_COMPONENT(g);
172 			*(ptr++) = NORM_COMPONENT(b);
173 
174 			if (z++) {
175 				z = 0;
176 				data += 4;
177 			}
178 		}
179 		data += padding;
180 
181 		JSAMPROW scanlines[1] = {line_buf};
182 		jpeg_write_scanlines(jpeg, scanlines, 1);
183 	}
184 
185 	free(line_buf);
186 }
187 
188 #undef NORM_COMPONENT
189 #undef YUV_B
190 #undef YUV_G
191 #undef YUV_R
192 
_jpeg_write_scanlines_rgb565(struct jpeg_compress_struct * jpeg,const frame_s * frame)193 static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
194 	uint8_t *line_buf;
195 	A_CALLOC(line_buf, frame->width * 3);
196 
197 	const unsigned padding = frame_get_padding(frame);
198 	const uint8_t *data = frame->data;
199 
200 	while (jpeg->next_scanline < frame->height) {
201 		uint8_t *ptr = line_buf;
202 
203 		for (unsigned x = 0; x < frame->width; ++x) {
204 			unsigned int two_byte = (data[1] << 8) + data[0];
205 
206 			*(ptr++) = data[1] & 248; // Red
207 			*(ptr++) = (uint8_t)((two_byte & 2016) >> 3); // Green
208 			*(ptr++) = (data[0] & 31) * 8; // Blue
209 
210 			data += 2;
211 		}
212 		data += padding;
213 
214 		JSAMPROW scanlines[1] = {line_buf};
215 		jpeg_write_scanlines(jpeg, scanlines, 1);
216 	}
217 
218 	free(line_buf);
219 }
220 
_jpeg_write_scanlines_rgb24(struct jpeg_compress_struct * jpeg,const frame_s * frame)221 static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
222 	const unsigned padding = frame_get_padding(frame);
223 	uint8_t *data = frame->data;
224 
225 	while (jpeg->next_scanline < frame->height) {
226 		JSAMPROW scanlines[1] = {data};
227 		jpeg_write_scanlines(jpeg, scanlines, 1);
228 
229 		data += (jpeg->next_scanline * frame->width * 3) + padding;
230 	}
231 }
232 
233 #define JPEG_OUTPUT_BUFFER_SIZE ((size_t)4096)
234 
_jpeg_init_destination(j_compress_ptr jpeg)235 static void _jpeg_init_destination(j_compress_ptr jpeg) {
236 	_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
237 
238 	// Allocate the output buffer - it will be released when done with image
239 	assert((dest->buf = (JOCTET *)(*jpeg->mem->alloc_small)(
240 		(j_common_ptr) jpeg, JPOOL_IMAGE, JPEG_OUTPUT_BUFFER_SIZE * sizeof(JOCTET)
241 	)));
242 
243 	dest->mgr.next_output_byte = dest->buf;
244 	dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE;
245 }
246 
_jpeg_empty_output_buffer(j_compress_ptr jpeg)247 static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
248 	// Called whenever local jpeg buffer fills up
249 
250 	_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
251 
252 	frame_append_data(dest->frame, dest->buf, JPEG_OUTPUT_BUFFER_SIZE);
253 
254 	dest->mgr.next_output_byte = dest->buf;
255 	dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE;
256 
257 	return TRUE;
258 }
259 
_jpeg_term_destination(j_compress_ptr jpeg)260 static void _jpeg_term_destination(j_compress_ptr jpeg) {
261 	// Called by jpeg_finish_compress after all data has been written.
262 	// Usually needs to flush buffer.
263 
264 	_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
265 	size_t final = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer;
266 
267 	// Write any data remaining in the buffer.
268 	frame_append_data(dest->frame, dest->buf, final);
269 }
270 
271 #undef JPEG_OUTPUT_BUFFER_SIZE
272