1 #include "config.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <pthread.h>
8 #include <jpeglib.h>
9
10 #include "grab-ng.h"
11
12 /* ---------------------------------------------------------------------- */
13
14 struct mjpeg_compress {
15 struct jpeg_destination_mgr mjpg_dest; /* must be first */
16 struct jpeg_compress_struct mjpg_cinfo;
17 struct jpeg_error_mgr mjpg_jerr;
18
19 struct ng_video_fmt fmt;
20
21 JOCTET *mjpg_buffer;
22 size_t mjpg_bufsize;
23 size_t mjpg_bufused;
24 int mjpg_tables;
25
26 /* yuv */
27 unsigned char **mjpg_ptrs[3];
28 };
29
30 struct mjpeg_decompress {
31 struct jpeg_source_mgr mjpg_src; /* must be first */
32 struct jpeg_decompress_struct mjpg_cinfo;
33 struct jpeg_error_mgr mjpg_jerr;
34
35 struct ng_video_fmt fmt;
36 struct ng_video_buf *buf;
37
38 /* yuv */
39 unsigned char **mjpg_ptrs[3];
40 };
41
42 struct mjpeg_yuv_priv {
43 int luma_h;
44 int luma_v;
45 };
46
47 static void
48 swap_rgb24(char *mem, int n)
49 {
50 char c;
51 char *p = mem;
52 int i = n;
53
54 while (--i) {
55 c = p[0]; p[0] = p[2]; p[2] = c;
56 p += 3;
57 }
58 }
59
60 /* ---------------------------------------------------------------------- */
61 /* I/O manager */
62
63 static void mjpg_dest_init(struct jpeg_compress_struct *cinfo)
64 {
65 struct mjpeg_compress *h = (struct mjpeg_compress*)cinfo->dest;
66 cinfo->dest->next_output_byte = h->mjpg_buffer;
67 cinfo->dest->free_in_buffer = h->mjpg_bufsize;
68 }
69
70 static boolean mjpg_dest_flush(struct jpeg_compress_struct *cinfo)
71 {
72 fprintf(stderr,"mjpg: panic: output buffer too small\n");
73 exit(1);
74 }
75
76 static void mjpg_dest_term(struct jpeg_compress_struct *cinfo)
77 {
78 struct mjpeg_compress *h = (struct mjpeg_compress*)cinfo->dest;
79 h->mjpg_bufused = h->mjpg_bufsize - cinfo->dest->free_in_buffer;
80 }
81
82 static void mjpg_src_init(struct jpeg_decompress_struct *cinfo)
83 {
84 struct mjpeg_decompress *h = (struct mjpeg_decompress*)cinfo->src;
85 cinfo->src->next_input_byte = h->buf->data;
86 cinfo->src->bytes_in_buffer = h->buf->size;
87 }
88
89 static int mjpg_src_fill(struct jpeg_decompress_struct *cinfo)
90 {
91 fprintf(stderr,"mjpg: panic: no more input data\n");
92 exit(1);
93 }
94
95 static void mjpg_src_skip(struct jpeg_decompress_struct *cinfo,
96 long num_bytes)
97 {
98 cinfo->src->next_input_byte += num_bytes;
99 }
100
101 static void mjpg_src_term(struct jpeg_decompress_struct *cinfo)
102 {
103 /* nothing */
104 }
105
106 /* ---------------------------------------------------------------------- */
107 /* compress */
108
109 static struct mjpeg_compress*
110 mjpg_init(struct ng_video_fmt *fmt)
111 {
112 struct mjpeg_compress *h;
113
114 h = malloc(sizeof(*h));
115 if (NULL == h)
116 return NULL;
117 memset(h,0,sizeof(*h));
118
119 h->mjpg_cinfo.err = jpeg_std_error(&h->mjpg_jerr);
120 jpeg_create_compress(&h->mjpg_cinfo);
121
122 h->mjpg_dest.init_destination = mjpg_dest_init;
123 h->mjpg_dest.empty_output_buffer = mjpg_dest_flush;
124 h->mjpg_dest.term_destination = mjpg_dest_term;
125 h->mjpg_cinfo.dest = &h->mjpg_dest;
126
127 h->fmt = *fmt;
128 h->mjpg_tables = TRUE;
129 h->mjpg_cinfo.image_width = fmt->width;
130 h->mjpg_cinfo.image_height = fmt->height;
131
132 h->mjpg_cinfo.image_width &= ~(2*DCTSIZE-1);
133 h->mjpg_cinfo.image_height &= ~(2*DCTSIZE-1);
134
135 return h;
136 }
137
138 static void
139 mjpg_cleanup(void *handle)
140 {
141 struct mjpeg_compress *h = handle;
142 int i;
143
144 if (ng_debug > 1)
145 fprintf(stderr,"mjpg_cleanup\n");
146
147 jpeg_destroy_compress(&h->mjpg_cinfo);
148 for (i = 0; i < 3; i++)
149 if (NULL != h->mjpg_ptrs[i])
150 free(h->mjpg_ptrs[i]);
151 free(h);
152 }
153
154 /* ---------------------------------------------------------------------- */
155
156 static void*
157 mjpg_rgb_init(struct ng_video_fmt *out, void *priv)
158 {
159 struct mjpeg_compress *h;
160
161 if (ng_debug > 1)
162 fprintf(stderr,"mjpg_rgb_init\n");
163
164 h = mjpg_init(out);
165 if (NULL == h)
166 return NULL;
167
168 h->mjpg_cinfo.input_components = 3;
169 h->mjpg_cinfo.in_color_space = JCS_RGB;
170
171 jpeg_set_defaults(&h->mjpg_cinfo);
172 h->mjpg_cinfo.dct_method = JDCT_FASTEST;
173 jpeg_set_quality(&h->mjpg_cinfo, ng_jpeg_quality, TRUE);
174 jpeg_suppress_tables(&h->mjpg_cinfo, TRUE);
175
176 return h;
177 }
178
179 static void
180 mjpg_rgb_compress(void *handle, struct ng_video_buf *out,
181 struct ng_video_buf *in)
182 {
183 struct mjpeg_compress *h = handle;
184 unsigned char *line;
185 unsigned int i;
186
187 if (ng_debug > 1)
188 fprintf(stderr,"mjpg_rgb_compress\n");
189
190 h->mjpg_buffer = out->data;
191 h->mjpg_bufsize = out->size;
192
193 jpeg_start_compress(&h->mjpg_cinfo, h->mjpg_tables);
194 for (i = 0, line = in->data; i < h->mjpg_cinfo.image_height;
195 i++, line += 3*h->mjpg_cinfo.image_width)
196 jpeg_write_scanlines(&h->mjpg_cinfo, &line, 1);
197 jpeg_finish_compress(&h->mjpg_cinfo);
198 out->size = h->mjpg_bufused;
199 }
200
201 static void
202 mjpg_bgr_compress(void *handle, struct ng_video_buf *out,
203 struct ng_video_buf *in)
204 {
205 swap_rgb24(in->data,in->fmt.width*in->fmt.height); /* FIXME */
206 return mjpg_rgb_compress(handle,out,in);
207 }
208
209 /* ---------------------------------------------------------------------- */
210
211 static void*
212 mjpg_yuv_init(struct ng_video_fmt *out, void *priv)
213 {
214 struct mjpeg_compress *h;
215 struct mjpeg_yuv_priv *c = priv;
216
217 if (ng_debug > 1)
218 fprintf(stderr,"mjpg_yuv_init\n");
219
220 h = mjpg_init(out);
221 if (NULL == h)
222 return NULL;
223
224 h->mjpg_cinfo.input_components = 3;
225 h->mjpg_cinfo.in_color_space = JCS_YCbCr;
226
227 jpeg_set_defaults(&h->mjpg_cinfo);
228 h->mjpg_cinfo.dct_method = JDCT_FASTEST;
229 jpeg_set_quality(&h->mjpg_cinfo, ng_jpeg_quality, TRUE);
230
231 h->mjpg_cinfo.raw_data_in = TRUE;
232 jpeg_set_colorspace(&h->mjpg_cinfo,JCS_YCbCr);
233
234 h->mjpg_ptrs[0] = malloc(h->fmt.height*sizeof(char*));
235 h->mjpg_ptrs[1] = malloc(h->fmt.height*sizeof(char*));
236 h->mjpg_ptrs[2] = malloc(h->fmt.height*sizeof(char*));
237
238 h->mjpg_cinfo.comp_info[0].h_samp_factor = c->luma_h;
239 h->mjpg_cinfo.comp_info[0].v_samp_factor = c->luma_v;
240 h->mjpg_cinfo.comp_info[1].h_samp_factor = 1;
241 h->mjpg_cinfo.comp_info[1].v_samp_factor = 1;
242 h->mjpg_cinfo.comp_info[2].h_samp_factor = 1;
243 h->mjpg_cinfo.comp_info[2].v_samp_factor = 1;
244
245 jpeg_suppress_tables(&h->mjpg_cinfo, TRUE);
246 return h;
247 }
248
249 static void
250 mjpg_420_compress(struct mjpeg_compress *h)
251 {
252 unsigned char **mjpg_run[3];
253 unsigned int y;
254
255 mjpg_run[0] = h->mjpg_ptrs[0];
256 mjpg_run[1] = h->mjpg_ptrs[1];
257 mjpg_run[2] = h->mjpg_ptrs[2];
258
259 jpeg_start_compress(&h->mjpg_cinfo, h->mjpg_tables);
260 for (y = 0; y < h->mjpg_cinfo.image_height; y += 2*DCTSIZE) {
261 jpeg_write_raw_data(&h->mjpg_cinfo, mjpg_run,2*DCTSIZE);
262 mjpg_run[0] += 2*DCTSIZE;
263 mjpg_run[1] += DCTSIZE;
264 mjpg_run[2] += DCTSIZE;
265 }
266 jpeg_finish_compress(&h->mjpg_cinfo);
267 }
268
269 static void
270 mjpg_422_compress(struct mjpeg_compress *h)
271 {
272 unsigned char **mjpg_run[3];
273 unsigned int y;
274
275 mjpg_run[0] = h->mjpg_ptrs[0];
276 mjpg_run[1] = h->mjpg_ptrs[1];
277 mjpg_run[2] = h->mjpg_ptrs[2];
278
279 h->mjpg_cinfo.write_JFIF_header = FALSE;
280 jpeg_start_compress(&h->mjpg_cinfo, h->mjpg_tables);
281 jpeg_write_marker(&h->mjpg_cinfo, JPEG_APP0, "AVI1\0\0\0\0", 8);
282 for (y = 0; y < h->mjpg_cinfo.image_height; y += DCTSIZE) {
283 jpeg_write_raw_data(&h->mjpg_cinfo, mjpg_run, DCTSIZE);
284 mjpg_run[0] += DCTSIZE;
285 mjpg_run[1] += DCTSIZE;
286 mjpg_run[2] += DCTSIZE;
287 }
288 jpeg_finish_compress(&h->mjpg_cinfo);
289 }
290
291 /* ---------------------------------------------------------------------- */
292
293 static void
294 mjpg_422_420_compress(void *handle, struct ng_video_buf *out,
295 struct ng_video_buf *in)
296 {
297 struct mjpeg_compress *h = handle;
298 unsigned char *line;
299 unsigned int i;
300
301 if (ng_debug > 1)
302 fprintf(stderr,"mjpg_422_420_compress\n");
303
304 h->mjpg_buffer = out->data;
305 h->mjpg_bufsize = out->size;
306
307 line = in->data;
308 for (i = 0; i < h->mjpg_cinfo.image_height; i++, line += in->fmt.width)
309 h->mjpg_ptrs[0][i] = line;
310
311 line = in->data + in->fmt.width*in->fmt.height;
312 for (i = 0; i < h->mjpg_cinfo.image_height; i+=2, line += in->fmt.width)
313 h->mjpg_ptrs[1][i/2] = line;
314
315 line = in->data + in->fmt.width*in->fmt.height*3/2;
316 for (i = 0; i < h->mjpg_cinfo.image_height; i+=2, line += in->fmt.width)
317 h->mjpg_ptrs[2][i/2] = line;
318
319 mjpg_420_compress(h);
320 out->size = h->mjpg_bufused;
321 }
322
323 static void
324 mjpg_420_420_compress(void *handle, struct ng_video_buf *out,
325 struct ng_video_buf *in)
326 {
327 struct mjpeg_compress *h = handle;
328 unsigned char *line;
329 unsigned int i;
330
331 if (ng_debug > 1)
332 fprintf(stderr,"mjpg_420_420_compress\n");
333
334 h->mjpg_buffer = out->data;
335 h->mjpg_bufsize = out->size;
336
337 line = in->data;
338 for (i = 0; i < h->mjpg_cinfo.image_height; i++, line += in->fmt.width)
339 h->mjpg_ptrs[0][i] = line;
340
341 line = in->data + in->fmt.width*in->fmt.height;
342 for (i = 0; i < h->mjpg_cinfo.image_height; i+=2, line += in->fmt.width/2)
343 h->mjpg_ptrs[1][i/2] = line;
344
345 line = in->data + in->fmt.width*in->fmt.height*5/4;
346 for (i = 0; i < h->mjpg_cinfo.image_height; i+=2, line += in->fmt.width/2)
347 h->mjpg_ptrs[2][i/2] = line;
348
349 mjpg_420_compress(h);
350 out->size = h->mjpg_bufused;
351 }
352
353 /* ---------------------------------------------------------------------- */
354
355 static void
356 mjpg_422_422_compress(void *handle, struct ng_video_buf *out,
357 struct ng_video_buf *in)
358 {
359 struct mjpeg_compress *h = handle;
360 unsigned char *line;
361 unsigned int i;
362
363 if (ng_debug > 1)
364 fprintf(stderr,"mjpg_422_422_compress\n");
365
366 h->mjpg_buffer = out->data;
367 h->mjpg_bufsize = out->size;
368
369 line = in->data;
370 for (i = 0; i < h->mjpg_cinfo.image_height; i++, line += in->fmt.width)
371 h->mjpg_ptrs[0][i] = line;
372
373 line = in->data + in->fmt.width*in->fmt.height;
374 for (i = 0; i < h->mjpg_cinfo.image_height; i++, line += in->fmt.width/2)
375 h->mjpg_ptrs[1][i] = line;
376
377 line = in->data + in->fmt.width*in->fmt.height*3/2;
378 for (i = 0; i < h->mjpg_cinfo.image_height; i++, line += in->fmt.width/2)
379 h->mjpg_ptrs[2][i] = line;
380
381 mjpg_422_compress(h);
382 out->size = h->mjpg_bufused;
383 }
384
385 /* ---------------------------------------------------------------------- */
386 /* decompress */
387
388 static void*
389 mjpg_de_init(struct ng_video_fmt *fmt, void *priv)
390 {
391 struct mjpeg_decompress *h;
392
393 h = malloc(sizeof(*h));
394 if (NULL == h)
395 return NULL;
396 memset(h,0,sizeof(*h));
397 h->fmt = *fmt;
398
399 h->mjpg_cinfo.err = jpeg_std_error(&h->mjpg_jerr);
400 jpeg_create_decompress(&h->mjpg_cinfo);
401
402 h->mjpg_src.init_source = mjpg_src_init;
403 h->mjpg_src.fill_input_buffer = mjpg_src_fill;
404 h->mjpg_src.skip_input_data = mjpg_src_skip;
405 h->mjpg_src.resync_to_restart = jpeg_resync_to_restart;
406 h->mjpg_src.term_source = mjpg_src_term;
407 h->mjpg_cinfo.src = &h->mjpg_src;
408
409 switch (h->fmt.fmtid) {
410 case VIDEO_YUV420P:
411 h->mjpg_ptrs[0] = malloc(h->fmt.height*sizeof(char*));
412 h->mjpg_ptrs[1] = malloc(h->fmt.height*sizeof(char*));
413 h->mjpg_ptrs[2] = malloc(h->fmt.height*sizeof(char*));
414 break;
415 }
416 return h;
417 }
418
419 static void
420 mjpg_rgb_decompress(void *handle, struct ng_video_buf *out,
421 struct ng_video_buf *in)
422 {
423 struct mjpeg_decompress *h = handle;
424 unsigned char *line;
425 unsigned int i;
426
427 if (ng_debug > 1)
428 fprintf(stderr,"mjpg_rgb_decompress\n");
429
430 h->buf = in;
431 jpeg_read_header(&h->mjpg_cinfo,1);
432 h->mjpg_cinfo.out_color_space = JCS_RGB;
433 jpeg_start_decompress(&h->mjpg_cinfo);
434 for (i = 0, line = out->data; i < out->fmt.height;
435 i++, line += out->fmt.bytesperline) {
436 jpeg_read_scanlines(&h->mjpg_cinfo, &line, 1);
437 }
438 jpeg_finish_decompress(&h->mjpg_cinfo);
439 }
440
441 static void
442 mjpg_yuv420_decompress(void *handle, struct ng_video_buf *out,
443 struct ng_video_buf *in)
444 {
445 struct mjpeg_decompress *h = handle;
446 unsigned char **mjpg_run[3];
447 unsigned char *line;
448 unsigned int i,y;
449
450 if (ng_debug > 1)
451 fprintf(stderr,"mjpg_yuv_decompress\n");
452
453 h->buf = in;
454 jpeg_read_header(&h->mjpg_cinfo,1);
455 h->mjpg_cinfo.raw_data_out = 1;
456
457 if (ng_debug > 1)
458 fprintf(stderr,"yuv: %dx%d - %d %d / %d %d / %d %d\n",
459 h->mjpg_cinfo.image_width,
460 h->mjpg_cinfo.image_height,
461 h->mjpg_cinfo.comp_info[0].h_samp_factor,
462 h->mjpg_cinfo.comp_info[0].v_samp_factor,
463 h->mjpg_cinfo.comp_info[1].h_samp_factor,
464 h->mjpg_cinfo.comp_info[1].v_samp_factor,
465 h->mjpg_cinfo.comp_info[2].h_samp_factor,
466 h->mjpg_cinfo.comp_info[2].v_samp_factor);
467
468 jpeg_start_decompress(&h->mjpg_cinfo);
469 mjpg_run[0] = h->mjpg_ptrs[0];
470 mjpg_run[1] = h->mjpg_ptrs[1];
471 mjpg_run[2] = h->mjpg_ptrs[2];
472
473 line = out->data;
474 for (i = 0; i < h->mjpg_cinfo.image_height; i++, line += out->fmt.width)
475 h->mjpg_ptrs[0][i] = line;
476
477 if (2 == h->mjpg_cinfo.comp_info[0].v_samp_factor) {
478 /* file has 420 -- all fine */
479 line = out->data + out->fmt.width*out->fmt.height;
480 for (i = 0; i < out->fmt.height; i+=2, line += out->fmt.width/2)
481 h->mjpg_ptrs[1][i/2] = line;
482
483 line = out->data + out->fmt.width*out->fmt.height*5/4;
484 for (i = 0; i < out->fmt.height; i+=2, line += out->fmt.width/2)
485 h->mjpg_ptrs[2][i/2] = line;
486
487 for (y = 0; y < out->fmt.height; y += 2*DCTSIZE) {
488 jpeg_read_raw_data(&h->mjpg_cinfo, mjpg_run,2*DCTSIZE);
489 mjpg_run[0] += 2*DCTSIZE;
490 mjpg_run[1] += DCTSIZE;
491 mjpg_run[2] += DCTSIZE;
492 }
493
494 } else {
495 /* file has 422 -- drop lines */
496 line = out->data + out->fmt.width*out->fmt.height;
497 for (i = 0; i < out->fmt.height; i+=2, line += out->fmt.width/2) {
498 h->mjpg_ptrs[1][i+0] = line;
499 h->mjpg_ptrs[1][i+1] = line;
500 }
501
502 line = out->data + out->fmt.width*out->fmt.height*5/4;
503 for (i = 0; i < out->fmt.height; i+=2, line += out->fmt.width/2) {
504 h->mjpg_ptrs[2][i+0] = line;
505 h->mjpg_ptrs[2][i+1] = line;
506 }
507
508 for (y = 0; y < h->mjpg_cinfo.image_height; y += DCTSIZE) {
509 jpeg_read_raw_data(&h->mjpg_cinfo, mjpg_run,DCTSIZE);
510 mjpg_run[0] += DCTSIZE;
511 mjpg_run[1] += DCTSIZE;
512 mjpg_run[2] += DCTSIZE;
513 }
514 }
515
516 jpeg_finish_decompress(&h->mjpg_cinfo);
517 }
518
519 static void
520 mjpg_de_cleanup(void *handle)
521 {
522 struct mjpeg_decompress *h = handle;
523
524 if (ng_debug > 1)
525 fprintf(stderr,"mjpg_de_cleanup\n");
526
527 jpeg_destroy_decompress(&h->mjpg_cinfo);
528 if (h->mjpg_ptrs[0])
529 free(h->mjpg_ptrs[0]);
530 if (h->mjpg_ptrs[1])
531 free(h->mjpg_ptrs[1]);
532 if (h->mjpg_ptrs[2])
533 free(h->mjpg_ptrs[2]);
534 free(h);
535 }
536
537 /* ---------------------------------------------------------------------- */
538 /* static data + register */
539
540 static struct mjpeg_yuv_priv priv_420 = {
541 luma_h: 2,
542 luma_v: 2,
543 };
544 static struct mjpeg_yuv_priv priv_422 = {
545 luma_h: 2,
546 luma_v: 1,
547 };
548
549 static struct ng_video_conv mjpg_list[] = {
550 {
551 /* --- compress --- */
552 init: mjpg_yuv_init,
553 frame: mjpg_420_420_compress,
554 fini: mjpg_cleanup,
555 fmtid_in: VIDEO_YUV420P,
556 fmtid_out: VIDEO_JPEG,
557 priv: &priv_420,
558 },{
559 init: mjpg_yuv_init,
560 frame: mjpg_422_420_compress,
561 fini: mjpg_cleanup,
562 fmtid_in: VIDEO_YUV422P,
563 fmtid_out: VIDEO_JPEG,
564 priv: &priv_420,
565 },{
566 init: mjpg_rgb_init,
567 frame: mjpg_rgb_compress,
568 fini: mjpg_cleanup,
569 fmtid_in: VIDEO_RGB24,
570 fmtid_out: VIDEO_JPEG,
571 },{
572 init: mjpg_rgb_init,
573 frame: mjpg_bgr_compress,
574 fini: mjpg_cleanup,
575 fmtid_in: VIDEO_BGR24,
576 fmtid_out: VIDEO_JPEG,
577 },{
578 init: mjpg_yuv_init,
579 frame: mjpg_422_422_compress,
580 fini: mjpg_cleanup,
581 fmtid_in: VIDEO_YUV422P,
582 fmtid_out: VIDEO_MJPEG,
583 priv: &priv_422,
584 },{
585 /* --- uncompress --- */
586 init: mjpg_de_init,
587 frame: mjpg_rgb_decompress,
588 fini: mjpg_de_cleanup,
589 fmtid_in: VIDEO_MJPEG,
590 fmtid_out: VIDEO_RGB24,
591 },{
592 init: mjpg_de_init,
593 frame: mjpg_rgb_decompress,
594 fini: mjpg_de_cleanup,
595 fmtid_in: VIDEO_JPEG,
596 fmtid_out: VIDEO_RGB24,
597 },{
598 init: mjpg_de_init,
599 frame: mjpg_yuv420_decompress,
600 fini: mjpg_de_cleanup,
601 fmtid_in: VIDEO_MJPEG,
602 fmtid_out: VIDEO_YUV420P,
603 },{
604 init: mjpg_de_init,
605 frame: mjpg_yuv420_decompress,
606 fini: mjpg_de_cleanup,
607 fmtid_in: VIDEO_JPEG,
608 fmtid_out: VIDEO_YUV420P,
609 }
610 };
611 static const int nconv = sizeof(mjpg_list)/sizeof(struct ng_video_conv);
612
613 extern void ng_plugin_init(void);
614 void ng_plugin_init(void)
615 {
616 ng_conv_register(NG_PLUGIN_MAGIC,__FILE__,mjpg_list,nconv);
617 }
618
|
This page was automatically generated by the
LXR engine.
|