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