gstjpegenc.c 10.9 KB
Newer Older
Christian Schaller's avatar
Christian Schaller committed
1
/* GStreamer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


21
22
23
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
24
25
26
#include <string.h>

#include "gstjpegenc.h"
Iain Holmes's avatar
Iain Holmes committed
27
#include <gst/video/video.h>
28
29
30

/* elementfactory information */
GstElementDetails gst_jpegenc_details = {
31
32
33
  "JPEG image encoder",
  "Codec/Encoder/Image",
  "Encode images in JPEG format",
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  "Wim Taymans <wim.taymans@tvd.be>",
};

/* JpegEnc signals and args */
enum {
  FRAME_ENCODED,
  /* FILL ME */
  LAST_SIGNAL
};

enum {
  ARG_0,
  /* FILL ME */
};

Iain Holmes's avatar
Iain Holmes committed
49
static void             gst_jpegenc_base_init   (gpointer g_class);
50
51
52
static void		gst_jpegenc_class_init	(GstJpegEnc *klass);
static void		gst_jpegenc_init	(GstJpegEnc *jpegenc);

53
static void		gst_jpegenc_chain	(GstPad *pad,GstData *_data);
54
55
static GstPadLinkReturn	gst_jpegenc_link	(GstPad *pad, GstCaps *caps);

56
static GstData	*gst_jpegenc_get	(GstPad *pad);
57
58
59
60
61

static void		gst_jpegenc_resync	(GstJpegEnc *jpegenc);

static GstElementClass *parent_class = NULL;
static guint gst_jpegenc_signals[LAST_SIGNAL] = { 0 };
Iain Holmes's avatar
Iain Holmes committed
62
static GstPadTemplate *jpegenc_src_template, *jpegenc_sink_template;
63
64
65
66
67
68
69
70

GType
gst_jpegenc_get_type (void)
{
  static GType jpegenc_type = 0;

  if (!jpegenc_type) {
    static const GTypeInfo jpegenc_info = {
Iain Holmes's avatar
Iain Holmes committed
71
72
      sizeof(GstJpegEnc),
      gst_jpegenc_base_init,
73
74
75
76
77
78
79
80
81
82
83
84
85
      NULL,
      (GClassInitFunc)gst_jpegenc_class_init,
      NULL,
      NULL,
      sizeof(GstJpegEnc),
      0,
      (GInstanceInitFunc)gst_jpegenc_init,
    };
    jpegenc_type = g_type_register_static(GST_TYPE_ELEMENT, "GstJpegEnc", &jpegenc_info, 0);
  }
  return jpegenc_type;
}

Iain Holmes's avatar
Iain Holmes committed
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
static GstCaps*
jpeg_caps_factory (void) 
{
  return
    gst_caps_new (
  	"jpeg_jpeg",
  	"video/x-jpeg",
  	    gst_props_new (
		"width",     GST_PROPS_INT_RANGE (16, 4096),
		"height",    GST_PROPS_INT_RANGE (16, 4096),
		"framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT),
                NULL));
}

static GstCaps*
raw_caps_factory (void)
{
  return
    gst_caps_new (
  	"jpeg_raw",
  	"video/x-raw-yuv",
	GST_VIDEO_YUV_PAD_TEMPLATE_PROPS (
	  GST_PROPS_FOURCC (GST_MAKE_FOURCC ('I','4','2','0'))));
}

static void
gst_jpegenc_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
  GstCaps *raw_caps, *jpeg_caps;
  
  raw_caps = raw_caps_factory ();
  jpeg_caps = jpeg_caps_factory ();

  jpegenc_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK, 
						GST_PAD_ALWAYS, 
						raw_caps, NULL);
  jpegenc_src_template = gst_pad_template_new ("src", GST_PAD_SRC, 
					       GST_PAD_ALWAYS, 
					       jpeg_caps, NULL);

  gst_element_class_add_pad_template (element_class, jpegenc_sink_template);
  gst_element_class_add_pad_template (element_class, jpegenc_src_template);
  gst_element_class_set_details (element_class, &gst_jpegenc_details);
}

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
static void
gst_jpegenc_class_init (GstJpegEnc *klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass*)klass;
  gstelement_class = (GstElementClass*)klass;

  parent_class = g_type_class_ref(GST_TYPE_ELEMENT);

  gst_jpegenc_signals[FRAME_ENCODED] =
    g_signal_new ("frame_encoded", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GstJpegEncClass, frame_encoded), NULL, NULL,
                   g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);

}

static void
gst_jpegenc_init_destination (j_compress_ptr cinfo)
{
153
  GST_DEBUG ("gst_jpegenc_chain: init_destination");
154
155
156
157
158
}

static gboolean
gst_jpegenc_flush_destination (j_compress_ptr cinfo)
{
159
  GST_DEBUG ("gst_jpegenc_chain: flush_destination: buffer too small !!!");
160
161
162
163
164
  return TRUE;
}

static void gst_jpegenc_term_destination (j_compress_ptr cinfo)
{
165
  GST_DEBUG ("gst_jpegenc_chain: term_source");
166
167
168
169
170
171
172
173
174
}

static void
gst_jpegenc_init (GstJpegEnc *jpegenc)
{
  /* create the sink and src pads */
  jpegenc->sinkpad = gst_pad_new("sink",GST_PAD_SINK);
  gst_element_add_pad(GST_ELEMENT(jpegenc),jpegenc->sinkpad);
  gst_pad_set_chain_function(jpegenc->sinkpad,gst_jpegenc_chain);
175
  gst_pad_set_link_function(jpegenc->sinkpad, gst_jpegenc_link);
176
177
178
179
  gst_pad_set_get_function(jpegenc->sinkpad,gst_jpegenc_get);
  jpegenc->srcpad = gst_pad_new("src",GST_PAD_SRC);
  gst_element_add_pad(GST_ELEMENT(jpegenc),jpegenc->srcpad);

Christian Schaller's avatar
Christian Schaller committed
180
  /* reset the initial video state */
181
182
183
184
185
186
187
188
189
  jpegenc->width = -1;
  jpegenc->height = -1;

  /* setup jpeglib */
  memset(&jpegenc->cinfo, 0, sizeof(jpegenc->cinfo));
  memset(&jpegenc->jerr, 0, sizeof(jpegenc->jerr));
  jpegenc->cinfo.err = jpeg_std_error(&jpegenc->jerr);
  jpeg_create_compress(&jpegenc->cinfo);

190
  GST_DEBUG ("gst_jpegenc_init: setting line buffers");
191
192
193
194
195
196
197
198
199
200
201
202
203
  jpegenc->line[0] = NULL;
  jpegenc->line[1] = NULL;
  jpegenc->line[2] = NULL;

  gst_jpegenc_resync(jpegenc);

  jpegenc->jdest.init_destination = gst_jpegenc_init_destination;
  jpegenc->jdest.empty_output_buffer = gst_jpegenc_flush_destination;
  jpegenc->jdest.term_destination = gst_jpegenc_term_destination;
  jpegenc->cinfo.dest = &jpegenc->jdest;

}

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
static GstPadLinkReturn
gst_jpegenc_link (GstPad *pad, GstCaps *caps)
{
  GstJpegEnc *jpegenc = GST_JPEGENC (gst_pad_get_parent (pad));

  if (!GST_CAPS_IS_FIXED (caps))
    return GST_PAD_LINK_DELAYED;

  gst_caps_get (caps,
		"framerate", &jpegenc->fps,
		"width",     &jpegenc->width,
		"height",    &jpegenc->height,
		NULL);

  caps = GST_CAPS_NEW ("jpegdec_srccaps",
		       "video/x-jpeg",
			 "width",     GST_PROPS_INT (jpegenc->width),
			 "height",    GST_PROPS_INT (jpegenc->height),
			 "framerate", GST_PROPS_FLOAT (jpegenc->fps));

  return gst_pad_try_set_caps (jpegenc->srcpad, caps);
}

227
228
229
230
231
232
static void
gst_jpegenc_resync (GstJpegEnc *jpegenc)
{
  guint size = 0;
  gint width, height;

233
  GST_DEBUG ("gst_jpegenc_resync: resync");
234
235
236
237
238

  jpegenc->cinfo.image_width = width = jpegenc->width;
  jpegenc->cinfo.image_height = height = jpegenc->height;
  jpegenc->cinfo.input_components = 3;

239
  GST_DEBUG ("gst_jpegenc_resync: wdith %d, height %d", width, height);
240
241
242

  jpeg_set_defaults(&jpegenc->cinfo);
  jpegenc->cinfo.dct_method = JDCT_FASTEST;
Christian Schaller's avatar
Christian Schaller committed
243
244
  /*jpegenc->cinfo.dct_method = JDCT_DEFAULT; */
  /*jpegenc->cinfo.smoothing_factor = 10; */
245
246
247
248
249
250
  jpeg_set_quality(&jpegenc->cinfo, 85, TRUE);

  /*
  switch (jpegenc->format) {
    case GST_COLORSPACE_RGB24:
      size = 3;
251
      GST_DEBUG ("gst_jpegenc_resync: setting format to RGB24");
252
253
254
255
256
257
258
      jpegenc->cinfo.in_color_space = JCS_RGB;
      jpegenc->cinfo.raw_data_in = FALSE;
      break;
    case GST_COLORSPACE_YUV420P:
      size = 2;
      jpegenc->cinfo.raw_data_in = TRUE;
      jpegenc->cinfo.in_color_space = JCS_YCbCr;
259
      GST_DEBUG ("gst_jpegenc_resync: setting format to YUV420P");
260
261
262
263
264
265
266
267
268
269
270
271
272
      jpegenc->cinfo.comp_info[0].h_samp_factor = 2;
      jpegenc->cinfo.comp_info[0].v_samp_factor = 2;
      jpegenc->cinfo.comp_info[1].h_samp_factor = 1;
      jpegenc->cinfo.comp_info[1].v_samp_factor = 1;
      jpegenc->cinfo.comp_info[2].h_samp_factor = 1;
      jpegenc->cinfo.comp_info[2].v_samp_factor = 1;

      if (height != -1) {
        jpegenc->line[0] = g_realloc(jpegenc->line[0], height*sizeof(char*));
        jpegenc->line[1] = g_realloc(jpegenc->line[1], height*sizeof(char*)/2);
        jpegenc->line[2] = g_realloc(jpegenc->line[2], height*sizeof(char*)/2);
      }

273
      GST_DEBUG ("gst_jpegenc_resync: setting format done");
274
275
276
277
278
279
280
281
282
283
284
285
286
287
      break;
    default:
      printf("gst_jpegenc_resync: unsupported colorspace, using RGB\n");
      size = 3;
      jpegenc->cinfo.in_color_space = JCS_RGB;
      break;
  }
*/
  jpegenc->bufsize = jpegenc->width*jpegenc->height*size;
  jpegenc->row_stride = width * size;

  jpeg_suppress_tables(&jpegenc->cinfo, TRUE);

  jpegenc->buffer = NULL;
288
  GST_DEBUG ("gst_jpegenc_resync: resync done");
289
290
}

291
static GstData*
292
293
294
295
296
gst_jpegenc_get (GstPad *pad)
{
  GstJpegEnc *jpegenc;
  GstBuffer *newbuf;

297
  GST_DEBUG ("gst_jpegenc_chain: pull buffer");
298
299
300
301
302
303

  g_return_val_if_fail (pad != NULL, NULL);
  g_return_val_if_fail (GST_IS_PAD (pad), NULL);

  jpegenc = GST_JPEGENC (GST_OBJECT_PARENT (pad));

Wim Taymans's avatar
Wim Taymans committed
304
  if (jpegenc->buffer == NULL || GST_BUFFER_REFCOUNT_VALUE(jpegenc->buffer) != 1) {
305
    if (jpegenc->buffer) gst_buffer_unref(jpegenc->buffer);
306
    GST_DEBUG ("gst_jpegenc_chain: new buffer");
307
308
309
310
311
312
    newbuf = jpegenc->buffer = gst_buffer_new();
    GST_BUFFER_DATA(newbuf) = g_malloc(jpegenc->bufsize);
    GST_BUFFER_SIZE(newbuf) = jpegenc->bufsize;
  }
  gst_buffer_ref(jpegenc->buffer);

313
  return GST_DATA (jpegenc->buffer);
314
315
316
}

static void
317
gst_jpegenc_chain (GstPad *pad, GstData *_data)
318
{
319
  GstBuffer *buf = GST_BUFFER (_data);
320
321
322
323
  GstJpegEnc *jpegenc;
  guchar *data, *outdata;
  gulong size, outsize;
  GstBuffer *outbuf;
Christian Schaller's avatar
Christian Schaller committed
324
/*  GstMeta *meta; */
325
326
327
328
329
330
331
  guint height, width, width2;
  guchar *base[3];
  gint i,j, k;

  g_return_if_fail (pad != NULL);
  g_return_if_fail (GST_IS_PAD (pad));
  g_return_if_fail (buf != NULL);
Christian Schaller's avatar
Christian Schaller committed
332
  /*g_return_if_fail(GST_IS_BUFFER(buf)); */
333

Christian Schaller's avatar
Christian Schaller committed
334
  /*usleep(10000); */
335
336
337
338
339
  jpegenc = GST_JPEGENC (GST_OBJECT_PARENT (pad));

  data = GST_BUFFER_DATA(buf);
  size = GST_BUFFER_SIZE(buf);

340
  GST_DEBUG ("gst_jpegenc_chain: got buffer of %ld bytes in '%s'",size,
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
          GST_OBJECT_NAME (jpegenc));

  outbuf = gst_buffer_new();
  outsize = GST_BUFFER_SIZE(outbuf) = jpegenc->bufsize;
  outdata = GST_BUFFER_DATA(outbuf) = g_malloc(outsize);
  GST_BUFFER_TIMESTAMP(outbuf) = GST_BUFFER_TIMESTAMP(buf);

  width = jpegenc->width;
  height = jpegenc->height;

  base[0] = data;
  base[1] = base[0]+width*height;
  base[2] = base[1]+width*height/4;

  jpegenc->jdest.next_output_byte = outdata;
  jpegenc->jdest.free_in_buffer = outsize;

  jpeg_start_compress(&jpegenc->cinfo, TRUE);

  width2 = width>>1;
361
  GST_DEBUG ("gst_jpegdec_chain: compressing");
362
363
364
365
366
367
368
369
370
371
372

  for (i = 0; i < height; i += 2*DCTSIZE) {
    for (j=0, k=0; j<2*DCTSIZE;j+=2, k++) {
      jpegenc->line[0][j]   = base[0]; base[0] += width;
      jpegenc->line[0][j+1] = base[0]; base[0] += width;
      jpegenc->line[1][k]   = base[1]; base[1] += width2;
      jpegenc->line[2][k]   = base[2]; base[2] += width2;
    }
    jpeg_write_raw_data(&jpegenc->cinfo, jpegenc->line, 2*DCTSIZE);
  }
  jpeg_finish_compress(&jpegenc->cinfo);
373
  GST_DEBUG ("gst_jpegdec_chain: compressing done");
374
375
376

  GST_BUFFER_SIZE(outbuf) = (((outsize - jpegenc->jdest.free_in_buffer)+3)&~3);

377
  gst_pad_push(jpegenc->srcpad, GST_DATA (outbuf));
378
379
380
381
382

  g_signal_emit(G_OBJECT(jpegenc),gst_jpegenc_signals[FRAME_ENCODED], 0);

  gst_buffer_unref(buf);
}