gst_plugins_good/ext/jpeg/gstsmokeenc.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     1 /* GStreamer
       
     2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
       
     3  *
       
     4  * This library is free software; you can redistribute it and/or
       
     5  * modify it under the terms of the GNU Library General Public
       
     6  * License as published by the Free Software Foundation; either
       
     7  * version 2 of the License, or (at your option) any later version.
       
     8  *
       
     9  * This library is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    12  * Library General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU Library General Public
       
    15  * License along with this library; if not, write to the
       
    16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    17  * Boston, MA 02111-1307, USA.
       
    18  */
       
    19 
       
    20 
       
    21 #ifdef HAVE_CONFIG_H
       
    22 #include "config.h"
       
    23 #endif
       
    24 #include <string.h>
       
    25 
       
    26 #include "gstsmokeenc.h"
       
    27 #include <gst/video/video.h>
       
    28 
       
    29 /* elementfactory information */
       
    30 static const GstElementDetails gst_smokeenc_details =
       
    31 GST_ELEMENT_DETAILS ("Smoke video encoder",
       
    32     "Codec/Encoder/Video",
       
    33     "Encode images into the Smoke format",
       
    34     "Wim Taymans <wim@fluendo.com>");
       
    35 
       
    36 GST_DEBUG_CATEGORY (smokeenc_debug);
       
    37 #define GST_CAT_DEFAULT smokeenc_debug
       
    38 
       
    39 #define SMOKEENC_DEFAULT_MIN_QUALITY 10
       
    40 #define SMOKEENC_DEFAULT_MAX_QUALITY 85
       
    41 #define SMOKEENC_DEFAULT_THRESHOLD 3000
       
    42 #define SMOKEENC_DEFAULT_KEYFRAME 20
       
    43 
       
    44 /* SmokeEnc signals and args */
       
    45 enum
       
    46 {
       
    47   FRAME_ENCODED,
       
    48   /* FILL ME */
       
    49   LAST_SIGNAL
       
    50 };
       
    51 
       
    52 enum
       
    53 {
       
    54   ARG_0,
       
    55   ARG_MIN_QUALITY,
       
    56   ARG_MAX_QUALITY,
       
    57   ARG_THRESHOLD,
       
    58   ARG_KEYFRAME
       
    59       /* FILL ME */
       
    60 };
       
    61 
       
    62 static void gst_smokeenc_base_init (gpointer g_class);
       
    63 static void gst_smokeenc_class_init (GstSmokeEnc * klass);
       
    64 static void gst_smokeenc_init (GstSmokeEnc * smokeenc);
       
    65 
       
    66 static GstFlowReturn gst_smokeenc_chain (GstPad * pad, GstBuffer * buf);
       
    67 static gboolean gst_smokeenc_setcaps (GstPad * pad, GstCaps * caps);
       
    68 static GstCaps *gst_smokeenc_getcaps (GstPad * pad);
       
    69 
       
    70 static void gst_smokeenc_resync (GstSmokeEnc * smokeenc);
       
    71 static void gst_smokeenc_set_property (GObject * object, guint prop_id,
       
    72     const GValue * value, GParamSpec * pspec);
       
    73 static void gst_smokeenc_get_property (GObject * object, guint prop_id,
       
    74     GValue * value, GParamSpec * pspec);
       
    75 
       
    76 static GstElementClass *parent_class = NULL;
       
    77 
       
    78 //static guint gst_smokeenc_signals[LAST_SIGNAL] = { 0 };
       
    79 
       
    80 GType
       
    81 gst_smokeenc_get_type (void)
       
    82 {
       
    83   static GType smokeenc_type = 0;
       
    84 
       
    85   if (!smokeenc_type) {
       
    86     static const GTypeInfo smokeenc_info = {
       
    87       sizeof (GstSmokeEncClass),
       
    88       (GBaseInitFunc) gst_smokeenc_base_init,
       
    89       NULL,
       
    90       (GClassInitFunc) gst_smokeenc_class_init,
       
    91       NULL,
       
    92       NULL,
       
    93       sizeof (GstSmokeEnc),
       
    94       0,
       
    95       (GInstanceInitFunc) gst_smokeenc_init,
       
    96     };
       
    97 
       
    98     smokeenc_type =
       
    99         g_type_register_static (GST_TYPE_ELEMENT, "GstSmokeEnc", &smokeenc_info,
       
   100         0);
       
   101   }
       
   102   return smokeenc_type;
       
   103 }
       
   104 
       
   105 static GstStaticPadTemplate gst_smokeenc_sink_pad_template =
       
   106 GST_STATIC_PAD_TEMPLATE ("sink",
       
   107     GST_PAD_SINK,
       
   108     GST_PAD_ALWAYS,
       
   109     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
       
   110     );
       
   111 
       
   112 static GstStaticPadTemplate gst_smokeenc_src_pad_template =
       
   113 GST_STATIC_PAD_TEMPLATE ("src",
       
   114     GST_PAD_SRC,
       
   115     GST_PAD_ALWAYS,
       
   116     GST_STATIC_CAPS ("video/x-smoke, "
       
   117         "width = (int) [ 16, 4096 ], "
       
   118         "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0/1, MAX ]")
       
   119     );
       
   120 
       
   121 static void
       
   122 gst_smokeenc_base_init (gpointer g_class)
       
   123 {
       
   124   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   125 
       
   126   gst_element_class_add_pad_template (element_class,
       
   127       gst_static_pad_template_get (&gst_smokeenc_sink_pad_template));
       
   128   gst_element_class_add_pad_template (element_class,
       
   129       gst_static_pad_template_get (&gst_smokeenc_src_pad_template));
       
   130   gst_element_class_set_details (element_class, &gst_smokeenc_details);
       
   131 }
       
   132 
       
   133 static void
       
   134 gst_smokeenc_class_init (GstSmokeEnc * klass)
       
   135 {
       
   136   GObjectClass *gobject_class;
       
   137   GstElementClass *gstelement_class;
       
   138 
       
   139   gobject_class = (GObjectClass *) klass;
       
   140   gstelement_class = (GstElementClass *) klass;
       
   141 
       
   142   parent_class = g_type_class_peek_parent (klass);
       
   143 
       
   144   gobject_class->set_property = gst_smokeenc_set_property;
       
   145   gobject_class->get_property = gst_smokeenc_get_property;
       
   146 
       
   147   g_object_class_install_property (gobject_class, ARG_MIN_QUALITY,
       
   148       g_param_spec_int ("qmin", "Qmin", "Minimum quality",
       
   149           0, 100, SMOKEENC_DEFAULT_MIN_QUALITY, G_PARAM_READWRITE));
       
   150   g_object_class_install_property (gobject_class, ARG_MAX_QUALITY,
       
   151       g_param_spec_int ("qmax", "Qmax", "Maximum quality",
       
   152           0, 100, SMOKEENC_DEFAULT_MAX_QUALITY, G_PARAM_READWRITE));
       
   153   g_object_class_install_property (gobject_class, ARG_THRESHOLD,
       
   154       g_param_spec_int ("threshold", "Threshold", "Motion estimation threshold",
       
   155           0, 100000000, SMOKEENC_DEFAULT_THRESHOLD, G_PARAM_READWRITE));
       
   156   g_object_class_install_property (gobject_class, ARG_KEYFRAME,
       
   157       g_param_spec_int ("keyframe", "Keyframe",
       
   158           "Insert keyframe every N frames", 1, 100000,
       
   159           SMOKEENC_DEFAULT_KEYFRAME, G_PARAM_READWRITE));
       
   160 
       
   161   GST_DEBUG_CATEGORY_INIT (smokeenc_debug, "smokeenc", 0,
       
   162       "Smoke encoding element");
       
   163 }
       
   164 
       
   165 static void
       
   166 gst_smokeenc_init (GstSmokeEnc * smokeenc)
       
   167 {
       
   168   /* create the sink and src pads */
       
   169   smokeenc->sinkpad =
       
   170       gst_pad_new_from_static_template (&gst_smokeenc_sink_pad_template,
       
   171       "sink");
       
   172   gst_pad_set_chain_function (smokeenc->sinkpad, gst_smokeenc_chain);
       
   173   gst_pad_set_getcaps_function (smokeenc->sinkpad, gst_smokeenc_getcaps);
       
   174   gst_pad_set_setcaps_function (smokeenc->sinkpad, gst_smokeenc_setcaps);
       
   175   gst_element_add_pad (GST_ELEMENT (smokeenc), smokeenc->sinkpad);
       
   176 
       
   177   smokeenc->srcpad =
       
   178       gst_pad_new_from_static_template (&gst_smokeenc_src_pad_template, "src");
       
   179   gst_pad_set_getcaps_function (smokeenc->sinkpad, gst_smokeenc_getcaps);
       
   180   gst_pad_use_fixed_caps (smokeenc->sinkpad);
       
   181   /*gst_pad_set_link_function (smokeenc->sinkpad, gst_smokeenc_link); */
       
   182   gst_element_add_pad (GST_ELEMENT (smokeenc), smokeenc->srcpad);
       
   183 
       
   184   /* reset the initial video state */
       
   185   smokeenc->width = 0;
       
   186   smokeenc->height = 0;
       
   187   smokeenc->frame = 0;
       
   188   smokeenc->need_header = TRUE;
       
   189 
       
   190   gst_smokeenc_resync (smokeenc);
       
   191 
       
   192   smokeenc->min_quality = SMOKEENC_DEFAULT_MIN_QUALITY;
       
   193   smokeenc->max_quality = SMOKEENC_DEFAULT_MAX_QUALITY;
       
   194   smokeenc->threshold = SMOKEENC_DEFAULT_THRESHOLD;
       
   195   smokeenc->keyframe = SMOKEENC_DEFAULT_KEYFRAME;
       
   196 }
       
   197 
       
   198 static GstCaps *
       
   199 gst_smokeenc_getcaps (GstPad * pad)
       
   200 {
       
   201   GstSmokeEnc *smokeenc = GST_SMOKEENC (gst_pad_get_parent (pad));
       
   202   GstPad *otherpad;
       
   203   GstCaps *caps;
       
   204   const char *name;
       
   205   int i;
       
   206   GstStructure *structure = NULL;
       
   207 
       
   208   /* we want to proxy properties like width, height and framerate from the
       
   209      other end of the element */
       
   210   otherpad = (pad == smokeenc->srcpad) ? smokeenc->sinkpad : smokeenc->srcpad;
       
   211   caps = gst_pad_get_allowed_caps (otherpad);
       
   212   if (pad == smokeenc->srcpad) {
       
   213     name = "image/x-smoke";
       
   214   } else {
       
   215     name = "video/x-raw-yuv";
       
   216   }
       
   217   for (i = 0; i < gst_caps_get_size (caps); i++) {
       
   218     structure = gst_caps_get_structure (caps, i);
       
   219 
       
   220     gst_structure_set_name (structure, name);
       
   221     gst_structure_remove_field (structure, "format");
       
   222     /* ... but for the sink pad, we only do I420 anyway, so add that */
       
   223     if (pad == smokeenc->sinkpad) {
       
   224       gst_structure_set (structure, "format", GST_TYPE_FOURCC,
       
   225           GST_STR_FOURCC ("I420"), NULL);
       
   226     }
       
   227   }
       
   228 
       
   229   gst_object_unref (smokeenc);
       
   230 
       
   231   return caps;
       
   232 }
       
   233 
       
   234 static gboolean
       
   235 gst_smokeenc_setcaps (GstPad * pad, GstCaps * caps)
       
   236 {
       
   237   GstSmokeEnc *smokeenc = GST_SMOKEENC (gst_pad_get_parent (pad));
       
   238   GstStructure *structure;
       
   239   gboolean ret = TRUE;
       
   240   GstCaps *othercaps;
       
   241   GstPad *otherpad;
       
   242   const GValue *framerate;
       
   243 
       
   244   otherpad = (pad == smokeenc->srcpad) ? smokeenc->sinkpad : smokeenc->srcpad;
       
   245 
       
   246   structure = gst_caps_get_structure (caps, 0);
       
   247   framerate = gst_structure_get_value (structure, "framerate");
       
   248   if (framerate) {
       
   249     smokeenc->fps_num = gst_value_get_fraction_numerator (framerate);
       
   250     smokeenc->fps_denom = gst_value_get_fraction_denominator (framerate);
       
   251   } else {
       
   252     smokeenc->fps_num = 0;
       
   253     smokeenc->fps_denom = 1;
       
   254   }
       
   255 
       
   256   gst_structure_get_int (structure, "width", &smokeenc->width);
       
   257   gst_structure_get_int (structure, "height", &smokeenc->height);
       
   258 
       
   259   othercaps = gst_caps_copy (gst_pad_get_pad_template_caps (otherpad));
       
   260   gst_caps_set_simple (othercaps,
       
   261       "width", G_TYPE_INT, smokeenc->width,
       
   262       "height", G_TYPE_INT, smokeenc->height,
       
   263       "framerate", GST_TYPE_FRACTION, smokeenc->fps_num, smokeenc->fps_denom,
       
   264       NULL);
       
   265 
       
   266   ret = gst_pad_set_caps (smokeenc->srcpad, othercaps);
       
   267   gst_caps_unref (othercaps);
       
   268 
       
   269   if (GST_PAD_LINK_SUCCESSFUL (ret)) {
       
   270     gst_smokeenc_resync (smokeenc);
       
   271   }
       
   272 
       
   273   gst_object_unref (smokeenc);
       
   274 
       
   275   return ret;
       
   276 }
       
   277 
       
   278 static void
       
   279 gst_smokeenc_resync (GstSmokeEnc * smokeenc)
       
   280 {
       
   281   GST_DEBUG ("gst_smokeenc_resync: resync");
       
   282 
       
   283   smokecodec_encode_new (&smokeenc->info, smokeenc->width, smokeenc->height,
       
   284       smokeenc->fps_num, smokeenc->fps_denom);
       
   285   smokecodec_set_quality (smokeenc->info, smokeenc->min_quality,
       
   286       smokeenc->max_quality);
       
   287 
       
   288   GST_DEBUG ("gst_smokeenc_resync: resync done");
       
   289 }
       
   290 
       
   291 static GstFlowReturn
       
   292 gst_smokeenc_chain (GstPad * pad, GstBuffer * buf)
       
   293 {
       
   294   GstSmokeEnc *smokeenc;
       
   295   guchar *data, *outdata;
       
   296   gulong size;
       
   297   gint outsize;
       
   298   guint encsize;
       
   299   GstBuffer *outbuf;
       
   300   SmokeCodecFlags flags;
       
   301   GstFlowReturn ret;
       
   302 
       
   303   smokeenc = GST_SMOKEENC (GST_OBJECT_PARENT (pad));
       
   304 
       
   305   data = GST_BUFFER_DATA (buf);
       
   306   size = GST_BUFFER_SIZE (buf);
       
   307 
       
   308   GST_DEBUG ("gst_smokeenc_chain: got buffer of %ld bytes in '%s'", size,
       
   309       GST_OBJECT_NAME (smokeenc));
       
   310 
       
   311   if (smokeenc->need_header) {
       
   312     outbuf = gst_buffer_new ();
       
   313     outsize = 256;
       
   314     outdata = g_malloc (outsize);
       
   315     GST_BUFFER_DATA (outbuf) = outdata;
       
   316     GST_BUFFER_MALLOCDATA (outbuf) = outdata;
       
   317     GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
       
   318     GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
       
   319 
       
   320     smokecodec_encode_id (smokeenc->info, outdata, &encsize);
       
   321 
       
   322     GST_BUFFER_SIZE (outbuf) = encsize;
       
   323 
       
   324     ret = gst_pad_push (smokeenc->srcpad, outbuf);
       
   325 
       
   326     smokeenc->need_header = FALSE;
       
   327   }
       
   328 
       
   329   outbuf = gst_buffer_new ();
       
   330   outsize = smokeenc->width * smokeenc->height * 3;
       
   331   outdata = g_malloc (outsize);
       
   332   GST_BUFFER_DATA (outbuf) = outdata;
       
   333   GST_BUFFER_MALLOCDATA (outbuf) = outdata;
       
   334   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
       
   335   GST_BUFFER_DURATION (outbuf) =
       
   336       smokeenc->fps_denom * GST_SECOND / smokeenc->fps_num;
       
   337 
       
   338   flags = 0;
       
   339   if ((smokeenc->frame % smokeenc->keyframe) == 0) {
       
   340     flags |= SMOKECODEC_KEYFRAME;
       
   341   }
       
   342   smokecodec_set_quality (smokeenc->info, smokeenc->min_quality,
       
   343       smokeenc->max_quality);
       
   344   smokecodec_set_threshold (smokeenc->info, smokeenc->threshold);
       
   345   smokecodec_encode (smokeenc->info, data, flags, outdata, &encsize);
       
   346   gst_buffer_unref (buf);
       
   347 
       
   348   GST_BUFFER_SIZE (outbuf) = encsize;
       
   349   GST_BUFFER_OFFSET (outbuf) = smokeenc->frame;
       
   350   GST_BUFFER_OFFSET_END (outbuf) = smokeenc->frame + 1;
       
   351 
       
   352   ret = gst_pad_push (smokeenc->srcpad, outbuf);
       
   353 
       
   354   smokeenc->frame++;
       
   355 
       
   356   return ret;
       
   357 }
       
   358 
       
   359 static void
       
   360 gst_smokeenc_set_property (GObject * object, guint prop_id,
       
   361     const GValue * value, GParamSpec * pspec)
       
   362 {
       
   363   GstSmokeEnc *smokeenc;
       
   364 
       
   365   g_return_if_fail (GST_IS_SMOKEENC (object));
       
   366   smokeenc = GST_SMOKEENC (object);
       
   367 
       
   368   switch (prop_id) {
       
   369     case ARG_MIN_QUALITY:
       
   370       smokeenc->min_quality = g_value_get_int (value);
       
   371       break;
       
   372     case ARG_MAX_QUALITY:
       
   373       smokeenc->max_quality = g_value_get_int (value);
       
   374       break;
       
   375     case ARG_THRESHOLD:
       
   376       smokeenc->threshold = g_value_get_int (value);
       
   377       break;
       
   378     case ARG_KEYFRAME:
       
   379       smokeenc->keyframe = g_value_get_int (value);
       
   380       break;
       
   381     default:
       
   382       break;
       
   383   }
       
   384 }
       
   385 
       
   386 static void
       
   387 gst_smokeenc_get_property (GObject * object, guint prop_id, GValue * value,
       
   388     GParamSpec * pspec)
       
   389 {
       
   390   GstSmokeEnc *smokeenc;
       
   391 
       
   392   g_return_if_fail (GST_IS_SMOKEENC (object));
       
   393   smokeenc = GST_SMOKEENC (object);
       
   394 
       
   395   switch (prop_id) {
       
   396     case ARG_MIN_QUALITY:
       
   397       g_value_set_int (value, smokeenc->min_quality);
       
   398       break;
       
   399     case ARG_MAX_QUALITY:
       
   400       g_value_set_int (value, smokeenc->max_quality);
       
   401       break;
       
   402     case ARG_THRESHOLD:
       
   403       g_value_set_int (value, smokeenc->threshold);
       
   404       break;
       
   405     case ARG_KEYFRAME:
       
   406       g_value_set_int (value, smokeenc->keyframe);
       
   407       break;
       
   408     default:
       
   409       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   410       break;
       
   411   }
       
   412 }