gsty4mencode.c 10 KB
Newer Older
1
/* GStreamer
2
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3
 * Copyright (C) <2006> Mark Nauwelaerts <mnauw@users.sourceforge.net>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * 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.
 */
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
 * SECTION:element-y4menc
 *
 * <refsect2>
 * <para>
 * Creates a YU4MPEG2 raw video stream as defined by the mjpegtools project.
 * </para>
 * <title>Example launch line</title>
 * <para>
 * (write everything in one line, without the backslash characters)
 * <programlisting>
 * gst-launch-0.10 videotestsrc num-buffers=250 \
 * ! 'video/x-raw-yuv,format=(fourcc)I420,width=320,height=240,framerate=(fraction)25/1' \
 * ! y4menc ! filesink location=test.yuv
 * </programlisting>
 * </para>
 * </refsect2>
 *
 */
39

40
41
/* see mjpegtools/yuv4mpeg.h for yuv4mpeg format */

42
43
44
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
45
46
#include <string.h>
#include <gst/gst.h>
47
#include <gst/video/video.h>
48
#include "gsty4mencode.h"
49
50

/* Filter signals and args */
51
52
enum
{
53
54
55
56
  /* FILL ME */
  LAST_SIGNAL
};

57
58
enum
{
59
60
61
  ARG_0
};

62
static GstStaticPadTemplate y4mencode_src_factory =
63
64
65
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
66
    GST_STATIC_CAPS ("application/x-yuv4mpeg, " "y4mversion = (int) 2")
67
    );
68

69
static GstStaticPadTemplate y4mencode_sink_factory =
70
71
72
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
Wim Taymans's avatar
Wim Taymans committed
73
    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ IYUV, I420, Y42B, Y41B, Y444 }"))
74
75
76
    );


77
static void gst_y4m_encode_set_property (GObject * object,
78
    guint prop_id, const GValue * value, GParamSpec * pspec);
79
static void gst_y4m_encode_get_property (GObject * object,
80
81
    guint prop_id, GValue * value, GParamSpec * pspec);

82
static void gst_y4m_encode_reset (GstY4mEncode * filter);
83

Wim Taymans's avatar
Wim Taymans committed
84
85
86
87
static gboolean gst_y4m_encode_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
static GstFlowReturn gst_y4m_encode_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buf);
88
89
static GstStateChangeReturn gst_y4m_encode_change_state (GstElement * element,
    GstStateChange transition);
90

Wim Taymans's avatar
Wim Taymans committed
91
92
#define gst_y4m_encode_parent_class parent_class
G_DEFINE_TYPE (GstY4mEncode, gst_y4m_encode, GST_TYPE_ELEMENT);
93

94
static void
95
gst_y4m_encode_class_init (GstY4mEncodeClass * klass)
96
97
98
99
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

100
101
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
102

Wim Taymans's avatar
Wim Taymans committed
103
104
105
  gobject_class->set_property = gst_y4m_encode_set_property;
  gobject_class->get_property = gst_y4m_encode_get_property;

106
107
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_y4m_encode_change_state);
108

Wim Taymans's avatar
Wim Taymans committed
109
110
111
112
113
114
115
116
117
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&y4mencode_src_factory));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&y4mencode_sink_factory));

  gst_element_class_set_details_simple (gstelement_class,
      "YUV4MPEG video encoder", "Codec/Encoder/Video",
      "Encodes a YUV frame into the yuv4mpeg format (mjpegtools)",
      "Wim Taymans <wim.taymans@gmail.com>");
118
119
120
}

static void
Wim Taymans's avatar
Wim Taymans committed
121
gst_y4m_encode_init (GstY4mEncode * filter)
122
{
123
  filter->sinkpad =
124
      gst_pad_new_from_static_template (&y4mencode_sink_factory, "sink");
125
  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
126
127
  gst_pad_set_chain_function (filter->sinkpad,
      GST_DEBUG_FUNCPTR (gst_y4m_encode_chain));
Wim Taymans's avatar
Wim Taymans committed
128
129
  gst_pad_set_event_function (filter->sinkpad,
      GST_DEBUG_FUNCPTR (gst_y4m_encode_sink_event));
130

131
  filter->srcpad =
132
      gst_pad_new_from_static_template (&y4mencode_src_factory, "src");
133
  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
134
  gst_pad_use_fixed_caps (filter->srcpad);
135

136
137
  /* init properties */
  gst_y4m_encode_reset (filter);
138
139
140
}

static void
141
142
gst_y4m_encode_reset (GstY4mEncode * filter)
{
Wim Taymans's avatar
Wim Taymans committed
143
  filter->negotiated = FALSE;
144
145
146
147
}

static gboolean
gst_y4m_encode_setcaps (GstPad * pad, GstCaps * vscaps)
148
{
Wim Taymans's avatar
Wim Taymans committed
149
  gboolean ret;
150
  GstY4mEncode *filter;
Wim Taymans's avatar
Wim Taymans committed
151
  GstVideoInfo info;
152
153
154

  filter = GST_Y4M_ENCODE (GST_PAD_PARENT (pad));

Wim Taymans's avatar
Wim Taymans committed
155
156
  if (!gst_video_info_from_caps (&info, vscaps))
    goto invalid_format;
157

Wim Taymans's avatar
Wim Taymans committed
158
  switch (GST_VIDEO_INFO_FORMAT (&info)) {
Wim Taymans's avatar
Wim Taymans committed
159
    case GST_VIDEO_FORMAT_I420:
160
161
      filter->colorspace = "420";
      break;
Wim Taymans's avatar
Wim Taymans committed
162
    case GST_VIDEO_FORMAT_Y42B:
163
164
      filter->colorspace = "422";
      break;
Wim Taymans's avatar
Wim Taymans committed
165
    case GST_VIDEO_FORMAT_Y41B:
166
167
      filter->colorspace = "411";
      break;
Wim Taymans's avatar
Wim Taymans committed
168
    case GST_VIDEO_FORMAT_Y444:
169
170
171
      filter->colorspace = "444";
      break;
    default:
Wim Taymans's avatar
Wim Taymans committed
172
      goto invalid_format;
173
  }
174

Wim Taymans's avatar
Wim Taymans committed
175
176
  filter->info = info;

177
  /* the template caps will do for the src pad, should always accept */
Wim Taymans's avatar
Wim Taymans committed
178
  ret = gst_pad_set_caps (filter->srcpad,
179
      gst_static_pad_template_get_caps (&y4mencode_src_factory));
Wim Taymans's avatar
Wim Taymans committed
180
181
182
183
184
185
186
187
188
189
190

  filter->negotiated = ret;

  return ret;

  /* ERRORS */
invalid_format:
  {
    GST_ERROR_OBJECT (filter, "got invalid caps");
    return FALSE;
  }
191
}
192

Wim Taymans's avatar
Wim Taymans committed
193
static gboolean
Wim Taymans's avatar
Wim Taymans committed
194
gst_y4m_encode_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
Wim Taymans's avatar
Wim Taymans committed
195
196
197
198
199
200
201
202
203
204
205
206
207
208
{
  gboolean res;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      res = gst_y4m_encode_setcaps (pad, caps);
      gst_event_unref (event);
      break;
    }
    default:
Wim Taymans's avatar
Wim Taymans committed
209
      res = gst_pad_event_default (pad, parent, event);
Wim Taymans's avatar
Wim Taymans committed
210
211
212
213
214
      break;
  }
  return res;
}

215
static inline GstBuffer *
Wim Taymans's avatar
Wim Taymans committed
216
gst_y4m_encode_get_stream_header (GstY4mEncode * filter, gboolean tff)
217
218
219
{
  gpointer header;
  GstBuffer *buf;
Zaheer Merali's avatar
Zaheer Merali committed
220
  gchar interlaced;
Wim Taymans's avatar
Wim Taymans committed
221
  gsize len;
Zaheer Merali's avatar
Zaheer Merali committed
222

Wim Taymans's avatar
Wim Taymans committed
223
224
225
226
227
228
229
230
  if (filter->info.flags & GST_VIDEO_FLAG_INTERLACED) {
    if (tff)
      interlaced = 't';
    else
      interlaced = 'b';
  } else {
    interlaced = 'p';
  }
231

232
  header = g_strdup_printf ("YUV4MPEG2 C%s W%d H%d I%c F%d:%d A%d:%d\n",
Wim Taymans's avatar
Wim Taymans committed
233
234
235
236
237
238
      filter->colorspace, GST_VIDEO_INFO_WIDTH (&filter->info),
      GST_VIDEO_INFO_HEIGHT (&filter->info), interlaced,
      GST_VIDEO_INFO_FPS_N (&filter->info),
      GST_VIDEO_INFO_FPS_D (&filter->info),
      GST_VIDEO_INFO_PAR_N (&filter->info),
      GST_VIDEO_INFO_PAR_D (&filter->info));
Wim Taymans's avatar
Wim Taymans committed
239
  len = strlen (header);
240

241
  buf = gst_buffer_new ();
Wim Taymans's avatar
Wim Taymans committed
242
  gst_buffer_take_memory (buf, -1,
Wim Taymans's avatar
Wim Taymans committed
243
      gst_memory_new_wrapped (0, header, len, 0, len, header, g_free));
244
245
246

  return buf;
}
247

248
249
250
251
252
static inline GstBuffer *
gst_y4m_encode_get_frame_header (GstY4mEncode * filter)
{
  gpointer header;
  GstBuffer *buf;
Wim Taymans's avatar
Wim Taymans committed
253
  gsize len;
254

255
  header = g_strdup_printf ("FRAME\n");
Wim Taymans's avatar
Wim Taymans committed
256
  len = strlen (header);
257

258
  buf = gst_buffer_new ();
Wim Taymans's avatar
Wim Taymans committed
259
  gst_buffer_take_memory (buf, -1,
Wim Taymans's avatar
Wim Taymans committed
260
      gst_memory_new_wrapped (0, header, len, 0, len, header, g_free));
261

262
263
264
265
  return buf;
}

static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
266
gst_y4m_encode_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
267
{
Wim Taymans's avatar
Wim Taymans committed
268
  GstY4mEncode *filter = GST_Y4M_ENCODE (parent);
269
  GstBuffer *outbuf;
270
  GstClockTime timestamp;
271
272

  /* check we got some decent info from caps */
Wim Taymans's avatar
Wim Taymans committed
273
  if (GST_VIDEO_INFO_FORMAT (&filter->info) == GST_VIDEO_FORMAT_UNKNOWN)
Wim Taymans's avatar
Wim Taymans committed
274
    goto not_negotiated;
275

276
277
  timestamp = GST_BUFFER_TIMESTAMP (buf);

278
  if (G_UNLIKELY (!filter->header)) {
Wim Taymans's avatar
Wim Taymans committed
279
280
281
    gboolean tff;

    if (filter->info.flags & GST_VIDEO_FLAG_INTERLACED) {
Wim Taymans's avatar
Wim Taymans committed
282
      tff = GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_FLAG_TFF);
Zaheer Merali's avatar
Zaheer Merali committed
283
    }
Wim Taymans's avatar
Wim Taymans committed
284
    outbuf = gst_y4m_encode_get_stream_header (filter, tff);
285
286
287
288
289
    filter->header = TRUE;
    outbuf = gst_buffer_join (outbuf, gst_y4m_encode_get_frame_header (filter));
  } else {
    outbuf = gst_y4m_encode_get_frame_header (filter);
  }
Wim Taymans's avatar
Wim Taymans committed
290
  /* join with data, FIXME, strides are all wrong etc */
291
292
  outbuf = gst_buffer_join (outbuf, buf);
  /* decorate */
Wim Taymans's avatar
Wim Taymans committed
293
  outbuf = gst_buffer_make_writable (outbuf);
294
295
296

  GST_BUFFER_TIMESTAMP (outbuf) = timestamp;

297
  return gst_pad_push (filter->srcpad, outbuf);
Wim Taymans's avatar
Wim Taymans committed
298
299
300
301

  /* ERRORS */
not_negotiated:
  {
Wim Taymans's avatar
Wim Taymans committed
302
    GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION, (NULL),
Wim Taymans's avatar
Wim Taymans committed
303
304
305
306
        ("format wasn't negotiated before chain function"));
    gst_buffer_unref (buf);
    return GST_FLOW_NOT_NEGOTIATED;
  }
307
308
309
}

static void
310
gst_y4m_encode_set_property (GObject * object, guint prop_id,
311
    const GValue * value, GParamSpec * pspec)
312
{
313
  GstY4mEncode G_GNUC_UNUSED *filter;
314

315
316
  g_return_if_fail (GST_IS_Y4M_ENCODE (object));
  filter = GST_Y4M_ENCODE (object);
317
318
319
320
321
322
323
324

  switch (prop_id) {
    default:
      break;
  }
}

static void
325
gst_y4m_encode_get_property (GObject * object, guint prop_id, GValue * value,
326
    GParamSpec * pspec)
327
{
328
  GstY4mEncode G_GNUC_UNUSED *filter;
329

330
331
  g_return_if_fail (GST_IS_Y4M_ENCODE (object));
  filter = GST_Y4M_ENCODE (object);
332
333
334
335
336
337
338
339

  switch (prop_id) {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

340
static GstStateChangeReturn
341
gst_y4m_encode_change_state (GstElement * element, GstStateChange transition)
342
{
343
344
  GstY4mEncode *filter = GST_Y4M_ENCODE (element);
  GstStateChangeReturn ret;
345

346
347
348
349
350
351
352
  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      break;
    default:
      break;
  }
353

354
355
356
357
  ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state,
      (element, transition), GST_STATE_CHANGE_SUCCESS);
  if (ret != GST_STATE_CHANGE_SUCCESS)
    return ret;
358

359
360
361
362
363
364
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      gst_y4m_encode_reset (filter);
      break;
    default:
      break;
365
366
  }

367
  return GST_STATE_CHANGE_SUCCESS;
368
369
370
}

static gboolean
371
plugin_init (GstPlugin * plugin)
372
{
373
  return gst_element_register (plugin, "y4menc", GST_RANK_PRIMARY,
374
      GST_TYPE_Y4M_ENCODE);
375
376
}

377
378
379
380
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "y4menc",
    "Encodes a YUV frame into the yuv4mpeg format (mjpegtools)",
381
    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)