gstffmpegcolorspace.c 10.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 * This file:
 * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

26
#include <gst/gst.h>
27 28 29 30 31 32 33 34
#ifdef HAVE_FFMPEG_UNINSTALLED
#include <avcodec.h>
#else
#include <ffmpeg/avcodec.h>
#endif

#include "gstffmpegcodecmap.h"

David Schleef's avatar
David Schleef committed
35 36 37 38
GST_DEBUG_CATEGORY_STATIC (debug_ffmpeg_csp);
#define GST_CAT_DEFAULT debug_ffmpeg_csp

#define GST_TYPE_FFMPEG_CSP \
39
  (gst_ffmpegcsp_get_type())
David Schleef's avatar
David Schleef committed
40 41 42 43 44 45 46 47
#define GST_FFMPEG_CSP(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEG_CSP,GstFFMpegCsp))
#define GST_FFMPEG_CSP_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEG_CSP,GstFFMpegCsp))
#define GST_IS_FFMPEG_CSP(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEG_CSP))
#define GST_IS_FFMPEG_CSP_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEG_CSP))
48 49 50 51 52

typedef struct _GstFFMpegCsp GstFFMpegCsp;
typedef struct _GstFFMpegCspClass GstFFMpegCspClass;

struct _GstFFMpegCsp {
David Schleef's avatar
David Schleef committed
53
  GstElement		element;
54

David Schleef's avatar
David Schleef committed
55 56 57
  GstPad *		sinkpad;
  GstPad *		srcpad;
  gboolean		need_caps_nego;
58

David Schleef's avatar
David Schleef committed
59 60 61 62 63 64 65 66
  gint			width;
  gint			height;
  gdouble		fps;
  
  enum PixelFormat	from_pixfmt;
  enum PixelFormat	to_pixfmt;
  AVFrame *		from_frame;
  AVFrame *		to_frame;
67 68 69 70 71 72 73 74 75
};

struct _GstFFMpegCspClass {
  GstElementClass parent_class;
};

/* elementfactory information */
static GstElementDetails ffmpegcsp_details = {
  "FFMPEG Colorspace converter",
76
  "Filter/Converter/Video",
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
  "Converts video from one colorspace to another",
  "Ronald Bultje <rbultje@ronald.bitfreak.net>",
};


/* Stereo signals and args */
enum {
  /* FILL ME */
  LAST_SIGNAL
};

enum {
  ARG_0,
};

static GType	gst_ffmpegcsp_get_type 		(void);

David Schleef's avatar
David Schleef committed
94 95 96
static void	gst_ffmpegcsp_base_init		(gpointer g_class);
static void	gst_ffmpegcsp_class_init	(gpointer g_class, gpointer class_data);
static void	gst_ffmpegcsp_init		(GTypeInstance *instance, gpointer g_class);
97 98

static GstPadLinkReturn
David Schleef's avatar
David Schleef committed
99 100
		gst_ffmpegcsp_connect		(GstPad		*pad,
						 const GstCaps *caps);
101
static GstPadLinkReturn
David Schleef's avatar
David Schleef committed
102 103 104
		gst_ffmpegcsp_try_connect 	(GstPad		*pad,
						 AVCodecContext	*ctx,
						 double		 fps);
105

David Schleef's avatar
David Schleef committed
106 107
static void	gst_ffmpegcsp_chain		(GstPad		*pad,
						 GstData	*data);
108
static GstElementStateReturn
David Schleef's avatar
David Schleef committed
109
		gst_ffmpegcsp_change_state 	(GstElement	*element);
110 111 112 113

static GstElementClass *parent_class = NULL;
/*static guint gst_ffmpegcsp_signals[LAST_SIGNAL] = { 0 }; */

David Schleef's avatar
David Schleef committed
114
/* does caps nego on a pad */
115
static GstPadLinkReturn
David Schleef's avatar
David Schleef committed
116
gst_ffmpegcsp_try_connect (GstPad *pad, AVCodecContext *ctx, double fps)
117
{
David Schleef's avatar
David Schleef committed
118
  gint i, ret;
119
  GstFFMpegCsp *space;
David Schleef's avatar
David Schleef committed
120 121 122 123
  gboolean try_all = (ctx->pix_fmt != PIX_FMT_NB);
  GstCaps *caps;
  
  space = GST_FFMPEG_CSP (gst_pad_get_parent (pad));
124

David Schleef's avatar
David Schleef committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
  /* loop over all possibilities and select the first one we can convert and
   * is accepted by the peer */
  caps = gst_ffmpeg_codectype_to_caps (CODEC_TYPE_VIDEO, ctx);
  for (i = 0; i < gst_caps_get_size (caps); i++) {
    GstStructure *structure = gst_caps_get_structure (caps, i);
    GstCaps *setcaps;

    if (fps > 0)
      gst_structure_set (structure, "framerate", G_TYPE_DOUBLE, fps, NULL);

    setcaps = gst_caps_new_full (gst_structure_copy (structure), NULL);
    
    ret = gst_pad_try_set_caps (pad, setcaps);
    gst_caps_free (setcaps);
    if (ret >= 0) {
      if (ctx->pix_fmt == PIX_FMT_NB)
	gst_ffmpeg_caps_to_codectype (CODEC_TYPE_VIDEO, caps, ctx);
      gst_caps_free (caps);
143

David Schleef's avatar
David Schleef committed
144
      return ret;
145 146 147
    }
  }

David Schleef's avatar
David Schleef committed
148
  if (try_all) {
149
    ctx->pix_fmt = PIX_FMT_NB;
David Schleef's avatar
David Schleef committed
150 151 152
    return gst_ffmpegcsp_try_connect (pad, ctx, fps);
  } else {
    return GST_PAD_LINK_REFUSED;
153 154 155 156
  }
}

static GstPadLinkReturn
David Schleef's avatar
David Schleef committed
157
gst_ffmpegcsp_connect (GstPad *pad, const GstCaps *caps)
158 159 160
{
  AVCodecContext *ctx;
  GstFFMpegCsp *space;
David Schleef's avatar
David Schleef committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
  gdouble fps;
  enum PixelFormat pixfmt;
  GstPad *other;
  enum PixelFormat *format, *other_format;

  space = GST_FFMPEG_CSP (gst_pad_get_parent (pad));

  if (space->sinkpad == pad) {
    other = space->srcpad;
    format = &space->from_pixfmt;
    other_format = &space->to_pixfmt;
  } else if (space->srcpad == pad) {
    other = space->sinkpad;
    format = &space->to_pixfmt;
    other_format = &space->from_pixfmt;
  } else {
    g_assert_not_reached ();
    return GST_PAD_LINK_REFUSED;
179 180 181 182 183 184 185 186
  }
  ctx = avcodec_alloc_context ();
  ctx->width = 0;
  ctx->height = 0;
  ctx->pix_fmt = PIX_FMT_NB;

  gst_ffmpeg_caps_to_codectype (CODEC_TYPE_VIDEO, caps, ctx);
  if (!ctx->width || !ctx->height || ctx->pix_fmt == PIX_FMT_NB) {
David Schleef's avatar
David Schleef committed
187
    av_free (ctx);
188 189 190
    return GST_PAD_LINK_REFUSED;
  }

David Schleef's avatar
David Schleef committed
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
  if (!gst_structure_get_double (gst_caps_get_structure (caps, 0), 
	"framerate", &fps))
    fps = 0;
  
  pixfmt = ctx->pix_fmt;
  if (*other_format == PIX_FMT_NB ||
      space->width != ctx->width ||
      space->height != ctx->height ||
      space->fps != fps) {
    GST_DEBUG_OBJECT (space, "Need caps nego on pad %s for size %dx%d", 
	GST_PAD_NAME (other), ctx->width, ctx->height);
    /* ctx->pix_fmt is set to preferred format */
    if (gst_ffmpegcsp_try_connect (space->sinkpad, ctx, fps) <= 0) {
      av_free (ctx);
      return GST_PAD_LINK_REFUSED;
    }
    *other_format = ctx->pix_fmt;
  }
209 210
  space->width = ctx->width;
  space->height = ctx->height;
David Schleef's avatar
David Schleef committed
211 212
  space->fps = fps;
  *format = pixfmt;
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  av_free (ctx);

  GST_INFO ( "size: %dx%d", space->width, space->height);

  return GST_PAD_LINK_OK;
}

static GType
gst_ffmpegcsp_get_type (void)
{
  static GType ffmpegcsp_type = 0;

  if (!ffmpegcsp_type) {
    static const GTypeInfo ffmpegcsp_info = {
      sizeof (GstFFMpegCspClass),
David Schleef's avatar
David Schleef committed
228
      gst_ffmpegcsp_base_init,
229
      NULL,
David Schleef's avatar
David Schleef committed
230
      gst_ffmpegcsp_class_init,
231 232 233 234
      NULL,
      NULL,
      sizeof (GstFFMpegCsp),
      0,
David Schleef's avatar
David Schleef committed
235
      gst_ffmpegcsp_init,
236 237 238 239 240
    };

    ffmpegcsp_type = g_type_register_static (GST_TYPE_ELEMENT,
					     "GstFFMpegColorspace",
					     &ffmpegcsp_info, 0);
David Schleef's avatar
David Schleef committed
241 242

    GST_DEBUG_CATEGORY_INIT (debug_ffmpeg_csp, "ffcolorspace", 0, "FFMpeg colorspace converter");
243 244 245 246 247
  }

  return ffmpegcsp_type;
}

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
248
static void
David Schleef's avatar
David Schleef committed
249
gst_ffmpegcsp_base_init (gpointer g_class)
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
250
{
David Schleef's avatar
David Schleef committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
  GstCaps *caps, *capscopy;
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

  /* template caps */
  caps = gst_ffmpeg_codectype_to_caps (CODEC_TYPE_VIDEO, NULL);
  capscopy = gst_caps_copy (caps);
  
  /* build templates */
  gst_element_class_add_pad_template (element_class, 
      gst_pad_template_new ("src",
			    GST_PAD_SRC,
			    GST_PAD_ALWAYS,
			    caps));
  gst_element_class_add_pad_template (element_class, 
      gst_pad_template_new ("sink",
			    GST_PAD_SINK,
			    GST_PAD_ALWAYS,
			    capscopy));
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
269 270 271 272

  gst_element_class_set_details (element_class, &ffmpegcsp_details);
}

273
static void
David Schleef's avatar
David Schleef committed
274
gst_ffmpegcsp_class_init (gpointer g_class, gpointer class_data)
275
{
David Schleef's avatar
David Schleef committed
276
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
277

David Schleef's avatar
David Schleef committed
278
  parent_class = g_type_class_peek_parent (g_class);
279 280 281 282 283

  gstelement_class->change_state = gst_ffmpegcsp_change_state;
}

static void
David Schleef's avatar
David Schleef committed
284
gst_ffmpegcsp_init (GTypeInstance *instance, gpointer g_class)
285
{
David Schleef's avatar
David Schleef committed
286 287 288 289 290 291
  GstFFMpegCsp *space = GST_FFMPEG_CSP (instance);
  
  space->sinkpad = gst_pad_new_from_template (
      gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (space), "sink"), 
      "sink");
  gst_pad_set_link_function (space->sinkpad, gst_ffmpegcsp_connect);
292 293 294
  gst_pad_set_chain_function (space->sinkpad,gst_ffmpegcsp_chain);
  gst_element_add_pad (GST_ELEMENT(space), space->sinkpad);

David Schleef's avatar
David Schleef committed
295 296 297
  space->srcpad = gst_pad_new_from_template (
      gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (space), "src"), 
      "src");
298
  gst_element_add_pad (GST_ELEMENT (space), space->srcpad);
David Schleef's avatar
David Schleef committed
299
  gst_pad_set_link_function (space->srcpad, gst_ffmpegcsp_connect);
300 301 302 303 304 305 306 307 308 309

  space->from_pixfmt = space->to_pixfmt = PIX_FMT_NB;
  space->from_frame = space->to_frame = NULL;
}

static void
gst_ffmpegcsp_chain (GstPad  *pad,
		     GstData *data)
{
  GstFFMpegCsp *space;
David Schleef's avatar
David Schleef committed
310
  GstBuffer *inbuf = GST_BUFFER (data);
311 312 313 314 315 316
  GstBuffer *outbuf = NULL;

  g_return_if_fail (pad != NULL);
  g_return_if_fail (GST_IS_PAD (pad));
  g_return_if_fail (inbuf != NULL);

David Schleef's avatar
David Schleef committed
317
  space = GST_FFMPEG_CSP (gst_pad_get_parent (pad));
318 319
  
  g_return_if_fail (space != NULL);
David Schleef's avatar
David Schleef committed
320
  g_return_if_fail (GST_IS_FFMPEG_CSP (space));
321 322 323 324 325 326 327 328 329 330

  if (space->from_pixfmt == PIX_FMT_NB ||
      space->to_pixfmt == PIX_FMT_NB) {
    gst_buffer_unref (inbuf);
    return;
  }

  if (space->from_pixfmt == space->to_pixfmt) {
    outbuf = inbuf;
  } else {
David Schleef's avatar
David Schleef committed
331 332 333 334 335
    guint size = avpicture_get_size (space->to_pixfmt,
				     space->width,
				     space->height);
    /* use bufferpools here */
    outbuf = gst_buffer_new_and_alloc (size);
336 337 338 339 340 341 342 343 344 345

    /* convert */
    avpicture_fill ((AVPicture *) space->from_frame, GST_BUFFER_DATA (inbuf),
		    space->from_pixfmt, space->width, space->height);
    avpicture_fill ((AVPicture *) space->to_frame, GST_BUFFER_DATA (outbuf),
		    space->to_pixfmt, space->width, space->height);
    img_convert ((AVPicture *) space->to_frame, space->to_pixfmt,
		 (AVPicture *) space->from_frame, space->from_pixfmt,
		 space->width, space->height);

David Schleef's avatar
David Schleef committed
346
    gst_buffer_stamp (outbuf, inbuf);
347 348 349 350 351 352 353 354 355 356 357 358

    gst_buffer_unref (inbuf);
  }

  gst_pad_push (space->srcpad, GST_DATA (outbuf));
}

static GstElementStateReturn
gst_ffmpegcsp_change_state (GstElement *element)
{
  GstFFMpegCsp *space;

David Schleef's avatar
David Schleef committed
359
  space = GST_FFMPEG_CSP (element);
360 361

  switch (GST_STATE_TRANSITION (element)) {
David Schleef's avatar
David Schleef committed
362 363
    case GST_STATE_READY_TO_PAUSED:
      space->need_caps_nego = TRUE;
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
      break;
    case GST_STATE_PAUSED_TO_READY:
      if (space->from_frame)
        av_free (space->from_frame);
      if (space->to_frame)
        av_free (space->to_frame);
      space->from_frame = NULL;
      space->to_frame = NULL;
      break;
  }

  if (parent_class->change_state)
    return parent_class->change_state (element);

  return GST_STATE_SUCCESS;
}

gboolean
gst_ffmpegcsp_register (GstPlugin *plugin)
{
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
384
  return gst_element_register (plugin, "ffcolorspace",
David Schleef's avatar
David Schleef committed
385
			       GST_RANK_NONE, GST_TYPE_FFMPEG_CSP);
386
}