mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
jpeg: Port jpegdec/jpegenc to base video classes
Conflicts: ext/jpeg/gstjpegdec.c ext/jpeg/gstjpegdec.h ext/jpeg/gstjpegenc.c ext/jpeg/gstjpegenc.h Reverted to 0.10 versions for now, next port again.
This commit is contained in:
parent
dd75c21670
commit
8e9eb77816
4 changed files with 448 additions and 1050 deletions
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,7 @@
|
||||||
/* GStreamer
|
/* GStreamer
|
||||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* Copyright (C) 2012 Collabora Ltd.
|
||||||
|
* Author : Edward Hervey <edward@collabora.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
@ -25,6 +27,7 @@
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
#include <gst/video/gstvideodecoder.h>
|
||||||
#include <gst/base/gstadapter.h>
|
#include <gst/base/gstadapter.h>
|
||||||
|
|
||||||
/* this is a hack hack hack to get around jpeglib header bugs... */
|
/* this is a hack hack hack to get around jpeglib header bugs... */
|
||||||
|
@ -64,44 +67,20 @@ struct GstJpegDecSourceMgr {
|
||||||
* doesn't handle the N buffers in, 1 buffer out case,
|
* doesn't handle the N buffers in, 1 buffer out case,
|
||||||
* but only the 1-in 1-out case */
|
* but only the 1-in 1-out case */
|
||||||
struct _GstJpegDec {
|
struct _GstJpegDec {
|
||||||
GstElement element;
|
GstVideoDecoder decoder;
|
||||||
|
|
||||||
/* pads */
|
|
||||||
GstPad *sinkpad;
|
|
||||||
GstPad *srcpad;
|
|
||||||
|
|
||||||
GstAdapter *adapter;
|
|
||||||
|
|
||||||
guint8 *cur_buf;
|
guint8 *cur_buf;
|
||||||
|
|
||||||
/* TRUE if each input buffer contains a whole jpeg image */
|
/* negotiated state */
|
||||||
gboolean packetized;
|
GstVideoCodecState *input_state;
|
||||||
|
GstVideoCodecFrame *current_frame;
|
||||||
|
|
||||||
/* the (expected) timestamp of the next frame */
|
gint offset[3];
|
||||||
guint64 next_ts;
|
gint stride;
|
||||||
|
gint inc;
|
||||||
GstSegment segment;
|
|
||||||
|
|
||||||
/* TRUE if the next output buffer should have the DISCONT flag set */
|
|
||||||
gboolean discont;
|
|
||||||
|
|
||||||
/* QoS stuff *//* with LOCK */
|
|
||||||
gdouble proportion;
|
|
||||||
GstClockTime earliest_time;
|
|
||||||
GstClockTime qos_duration;
|
|
||||||
|
|
||||||
/* input state */
|
|
||||||
gint in_fps_n;
|
|
||||||
gint in_fps_d;
|
|
||||||
|
|
||||||
/* negotiated output state */
|
|
||||||
GstBufferPool *pool;
|
|
||||||
GstVideoInfo info;
|
|
||||||
GstClockTime duration;
|
|
||||||
|
|
||||||
gint clrspc;
|
|
||||||
|
|
||||||
/* parse state */
|
/* parse state */
|
||||||
|
gboolean saw_header;
|
||||||
gint parse_offset;
|
gint parse_offset;
|
||||||
gint parse_entropy_len;
|
gint parse_entropy_len;
|
||||||
gint parse_resync;
|
gint parse_resync;
|
||||||
|
@ -118,9 +97,6 @@ struct _GstJpegDec {
|
||||||
/* number of errors since start or last successfully decoded image */
|
/* number of errors since start or last successfully decoded image */
|
||||||
guint error_count;
|
guint error_count;
|
||||||
|
|
||||||
/* number of successfully decoded images since start */
|
|
||||||
guint good_count;
|
|
||||||
|
|
||||||
struct jpeg_decompress_struct cinfo;
|
struct jpeg_decompress_struct cinfo;
|
||||||
struct GstJpegDecErrorMgr jerr;
|
struct GstJpegDecErrorMgr jerr;
|
||||||
struct GstJpegDecSourceMgr jsrc;
|
struct GstJpegDecSourceMgr jsrc;
|
||||||
|
@ -133,7 +109,7 @@ struct _GstJpegDec {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstJpegDecClass {
|
struct _GstJpegDecClass {
|
||||||
GstElementClass parent_class;
|
GstVideoDecoderClass decoder_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gst_jpeg_dec_get_type(void);
|
GType gst_jpeg_dec_get_type(void);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/* GStreamer
|
/* GStreamer
|
||||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* Copyright (C) 2012 Collabora Ltd.
|
||||||
|
* Author : Edward Hervey <edward@collabora.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
@ -38,6 +40,7 @@
|
||||||
#include "gstjpegenc.h"
|
#include "gstjpegenc.h"
|
||||||
#include "gstjpeg.h"
|
#include "gstjpeg.h"
|
||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
#include <gst/video/gstvideometa.h>
|
||||||
|
|
||||||
/* experimental */
|
/* experimental */
|
||||||
/* setting smoothig seems to have no effect in libjepeg
|
/* setting smoothig seems to have no effect in libjepeg
|
||||||
|
@ -69,24 +72,25 @@ enum
|
||||||
static void gst_jpegenc_reset (GstJpegEnc * enc);
|
static void gst_jpegenc_reset (GstJpegEnc * enc);
|
||||||
static void gst_jpegenc_finalize (GObject * object);
|
static void gst_jpegenc_finalize (GObject * object);
|
||||||
|
|
||||||
static GstFlowReturn gst_jpegenc_chain (GstPad * pad, GstObject * parent,
|
|
||||||
GstBuffer * buf);
|
|
||||||
static gboolean gst_jpegenc_sink_event (GstPad * pad, GstObject * parent,
|
|
||||||
GstEvent * event);
|
|
||||||
static GstCaps *gst_jpegenc_getcaps (GstPad * pad, GstCaps * filter);
|
|
||||||
static gboolean gst_jpegenc_sink_query (GstPad * pad, GstObject * parent,
|
|
||||||
GstQuery * query);
|
|
||||||
|
|
||||||
static void gst_jpegenc_resync (GstJpegEnc * jpegenc);
|
static void gst_jpegenc_resync (GstJpegEnc * jpegenc);
|
||||||
static void gst_jpegenc_set_property (GObject * object, guint prop_id,
|
static void gst_jpegenc_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec);
|
const GValue * value, GParamSpec * pspec);
|
||||||
static void gst_jpegenc_get_property (GObject * object, guint prop_id,
|
static void gst_jpegenc_get_property (GObject * object, guint prop_id,
|
||||||
GValue * value, GParamSpec * pspec);
|
GValue * value, GParamSpec * pspec);
|
||||||
static GstStateChangeReturn gst_jpegenc_change_state (GstElement * element,
|
|
||||||
GstStateChange transition);
|
static gboolean gst_jpegenc_start (GstVideoEncoder * benc);
|
||||||
|
static gboolean gst_jpegenc_stop (GstVideoEncoder * benc);
|
||||||
|
static gboolean gst_jpegenc_set_format (GstVideoEncoder * encoder,
|
||||||
|
GstVideoCodecState * state);
|
||||||
|
static GstFlowReturn gst_jpegenc_handle_frame (GstVideoEncoder * encoder,
|
||||||
|
GstVideoCodecFrame * frame);
|
||||||
|
static gboolean gst_jpegenc_propose_allocation (GstVideoEncoder * encoder,
|
||||||
|
GstQuery * query);
|
||||||
|
|
||||||
|
/* static guint gst_jpegenc_signals[LAST_SIGNAL] = { 0 }; */
|
||||||
|
|
||||||
#define gst_jpegenc_parent_class parent_class
|
#define gst_jpegenc_parent_class parent_class
|
||||||
G_DEFINE_TYPE (GstJpegEnc, gst_jpegenc, GST_TYPE_ELEMENT);
|
G_DEFINE_TYPE (GstJpegEnc, gst_jpegenc, GST_TYPE_VIDEO_ENCODER);
|
||||||
|
|
||||||
/* *INDENT-OFF* */
|
/* *INDENT-OFF* */
|
||||||
static GstStaticPadTemplate gst_jpegenc_sink_pad_template =
|
static GstStaticPadTemplate gst_jpegenc_sink_pad_template =
|
||||||
|
@ -108,15 +112,18 @@ GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
"height = (int) [ 16, 65535 ], " "framerate = (fraction) [ 0/1, MAX ]")
|
"height = (int) [ 16, 65535 ], " "framerate = (fraction) [ 0/1, MAX ]")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_jpegenc_class_init (GstJpegEncClass * klass)
|
gst_jpegenc_class_init (GstJpegEncClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class;
|
GObjectClass *gobject_class;
|
||||||
GstElementClass *gstelement_class;
|
GstElementClass *element_class;
|
||||||
|
GstVideoEncoderClass *venc_class;
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
gobject_class = (GObjectClass *) klass;
|
||||||
gstelement_class = (GstElementClass *) klass;
|
element_class = (GstElementClass *) klass;
|
||||||
|
venc_class = (GstVideoEncoderClass *) klass;
|
||||||
|
|
||||||
|
parent_class = g_type_class_peek_parent (klass);
|
||||||
|
|
||||||
gobject_class->finalize = gst_jpegenc_finalize;
|
gobject_class->finalize = gst_jpegenc_finalize;
|
||||||
gobject_class->set_property = gst_jpegenc_set_property;
|
gobject_class->set_property = gst_jpegenc_set_property;
|
||||||
|
@ -141,20 +148,30 @@ gst_jpegenc_class_init (GstJpegEncClass * klass)
|
||||||
JPEG_DEFAULT_IDCT_METHOD,
|
JPEG_DEFAULT_IDCT_METHOD,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
gstelement_class->change_state = gst_jpegenc_change_state;
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
|
||||||
gst_element_class_add_pad_template (gstelement_class,
|
|
||||||
gst_static_pad_template_get (&gst_jpegenc_sink_pad_template));
|
gst_static_pad_template_get (&gst_jpegenc_sink_pad_template));
|
||||||
gst_element_class_add_pad_template (gstelement_class,
|
gst_element_class_add_pad_template (element_class,
|
||||||
gst_static_pad_template_get (&gst_jpegenc_src_pad_template));
|
gst_static_pad_template_get (&gst_jpegenc_src_pad_template));
|
||||||
gst_element_class_set_static_metadata (gstelement_class, "JPEG image encoder",
|
gst_element_class_set_details_simple (element_class, "JPEG image encoder",
|
||||||
"Codec/Encoder/Image",
|
"Codec/Encoder/Image",
|
||||||
"Encode images in JPEG format", "Wim Taymans <wim.taymans@tvd.be>");
|
"Encode images in JPEG format", "Wim Taymans <wim.taymans@tvd.be>");
|
||||||
|
|
||||||
|
venc_class->start = gst_jpegenc_start;
|
||||||
|
venc_class->stop = gst_jpegenc_stop;
|
||||||
|
venc_class->set_format = gst_jpegenc_set_format;
|
||||||
|
venc_class->handle_frame = gst_jpegenc_handle_frame;
|
||||||
|
venc_class->propose_allocation = gst_jpegenc_propose_allocation;
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (jpegenc_debug, "jpegenc", 0,
|
GST_DEBUG_CATEGORY_INIT (jpegenc_debug, "jpegenc", 0,
|
||||||
"JPEG encoding element");
|
"JPEG encoding element");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_jpegenc_init_destination (j_compress_ptr cinfo)
|
||||||
|
{
|
||||||
|
GST_DEBUG ("gst_jpegenc_chain: init_destination");
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ensure_memory (GstJpegEnc * jpegenc)
|
ensure_memory (GstJpegEnc * jpegenc)
|
||||||
{
|
{
|
||||||
|
@ -193,12 +210,6 @@ ensure_memory (GstJpegEnc * jpegenc)
|
||||||
jpegenc->jdest.free_in_buffer = new_size - old_size;
|
jpegenc->jdest.free_in_buffer = new_size - old_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
gst_jpegenc_init_destination (j_compress_ptr cinfo)
|
|
||||||
{
|
|
||||||
GST_DEBUG ("gst_jpegenc_chain: init_destination");
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean
|
static boolean
|
||||||
gst_jpegenc_flush_destination (j_compress_ptr cinfo)
|
gst_jpegenc_flush_destination (j_compress_ptr cinfo)
|
||||||
{
|
{
|
||||||
|
@ -224,30 +235,17 @@ gst_jpegenc_term_destination (j_compress_ptr cinfo)
|
||||||
jpegenc->output_map.size - jpegenc->jdest.free_in_buffer);
|
jpegenc->output_map.size - jpegenc->jdest.free_in_buffer);
|
||||||
jpegenc->output_map.data = NULL;
|
jpegenc->output_map.data = NULL;
|
||||||
jpegenc->output_map.size = 0;
|
jpegenc->output_map.size = 0;
|
||||||
|
|
||||||
|
gst_video_frame_unmap (&jpegenc->current_vframe);
|
||||||
|
|
||||||
|
gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (jpegenc),
|
||||||
|
jpegenc->current_frame);
|
||||||
|
jpegenc->current_frame = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_jpegenc_init (GstJpegEnc * jpegenc)
|
gst_jpegenc_init (GstJpegEnc * jpegenc)
|
||||||
{
|
{
|
||||||
/* create the sink and src pads */
|
|
||||||
jpegenc->sinkpad =
|
|
||||||
gst_pad_new_from_static_template (&gst_jpegenc_sink_pad_template, "sink");
|
|
||||||
gst_pad_set_chain_function (jpegenc->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (gst_jpegenc_chain));
|
|
||||||
gst_pad_set_query_function (jpegenc->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (gst_jpegenc_sink_query));
|
|
||||||
gst_pad_set_event_function (jpegenc->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (gst_jpegenc_sink_event));
|
|
||||||
gst_element_add_pad (GST_ELEMENT (jpegenc), jpegenc->sinkpad);
|
|
||||||
|
|
||||||
jpegenc->srcpad =
|
|
||||||
gst_pad_new_from_static_template (&gst_jpegenc_src_pad_template, "src");
|
|
||||||
gst_pad_use_fixed_caps (jpegenc->srcpad);
|
|
||||||
gst_element_add_pad (GST_ELEMENT (jpegenc), jpegenc->srcpad);
|
|
||||||
|
|
||||||
/* reset the initial video state */
|
|
||||||
gst_video_info_init (&jpegenc->info);
|
|
||||||
|
|
||||||
/* setup jpeglib */
|
/* setup jpeglib */
|
||||||
memset (&jpegenc->cinfo, 0, sizeof (jpegenc->cinfo));
|
memset (&jpegenc->cinfo, 0, sizeof (jpegenc->cinfo));
|
||||||
memset (&jpegenc->jerr, 0, sizeof (jpegenc->jerr));
|
memset (&jpegenc->jerr, 0, sizeof (jpegenc->jerr));
|
||||||
|
@ -285,8 +283,6 @@ gst_jpegenc_reset (GstJpegEnc * enc)
|
||||||
enc->row[i][j] = NULL;
|
enc->row[i][j] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_video_info_init (&enc->info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -296,220 +292,92 @@ gst_jpegenc_finalize (GObject * object)
|
||||||
|
|
||||||
jpeg_destroy_compress (&filter->cinfo);
|
jpeg_destroy_compress (&filter->cinfo);
|
||||||
|
|
||||||
|
if (filter->input_state)
|
||||||
|
gst_video_codec_state_unref (filter->input_state);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstCaps *
|
|
||||||
gst_jpegenc_getcaps (GstPad * pad, GstCaps * filter)
|
|
||||||
{
|
|
||||||
GstJpegEnc *jpegenc = GST_JPEGENC (gst_pad_get_parent (pad));
|
|
||||||
GstCaps *caps, *othercaps;
|
|
||||||
GstCaps *otherfilter;
|
|
||||||
GstCaps *templ;
|
|
||||||
gint i, j;
|
|
||||||
GstStructure *structure = NULL;
|
|
||||||
|
|
||||||
/* we want to proxy properties like width, height and framerate from the
|
|
||||||
other end of the element */
|
|
||||||
if (filter) {
|
|
||||||
otherfilter = gst_caps_new_empty ();
|
|
||||||
for (i = 0; i < gst_caps_get_size (filter); i++) {
|
|
||||||
GstStructure *s = gst_structure_copy (gst_caps_get_structure (filter, i));
|
|
||||||
|
|
||||||
gst_structure_set_name (s, "image/jpeg");
|
|
||||||
|
|
||||||
gst_caps_append_structure (otherfilter, s);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
otherfilter = NULL;
|
|
||||||
}
|
|
||||||
othercaps = gst_pad_peer_query_caps (jpegenc->srcpad, otherfilter);
|
|
||||||
if (otherfilter)
|
|
||||||
gst_caps_unref (otherfilter);
|
|
||||||
|
|
||||||
templ = gst_pad_get_pad_template_caps (pad);
|
|
||||||
if (othercaps == NULL ||
|
|
||||||
gst_caps_is_empty (othercaps) || gst_caps_is_any (othercaps)) {
|
|
||||||
caps = templ;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
caps = gst_caps_new_empty ();
|
|
||||||
|
|
||||||
for (i = 0; i < gst_caps_get_size (templ); i++) {
|
|
||||||
/* pick fields from peer caps */
|
|
||||||
for (j = 0; j < gst_caps_get_size (othercaps); j++) {
|
|
||||||
GstStructure *s = gst_caps_get_structure (othercaps, j);
|
|
||||||
const GValue *val;
|
|
||||||
|
|
||||||
structure = gst_structure_copy (gst_caps_get_structure (templ, i));
|
|
||||||
if ((val = gst_structure_get_value (s, "width")))
|
|
||||||
gst_structure_set_value (structure, "width", val);
|
|
||||||
if ((val = gst_structure_get_value (s, "height")))
|
|
||||||
gst_structure_set_value (structure, "height", val);
|
|
||||||
if ((val = gst_structure_get_value (s, "framerate")))
|
|
||||||
gst_structure_set_value (structure, "framerate", val);
|
|
||||||
|
|
||||||
caps = gst_caps_merge_structure (caps, structure);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_caps_unref (templ);
|
|
||||||
|
|
||||||
done:
|
|
||||||
|
|
||||||
gst_caps_replace (&othercaps, NULL);
|
|
||||||
gst_object_unref (jpegenc);
|
|
||||||
|
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_jpegenc_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
|
gst_jpegenc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state)
|
||||||
{
|
{
|
||||||
gboolean res;
|
GstJpegEnc *enc = GST_JPEGENC (encoder);
|
||||||
|
|
||||||
switch (GST_QUERY_TYPE (query)) {
|
|
||||||
case GST_QUERY_CAPS:
|
|
||||||
{
|
|
||||||
GstCaps *filter, *caps;
|
|
||||||
|
|
||||||
gst_query_parse_caps (query, &filter);
|
|
||||||
caps = gst_jpegenc_getcaps (pad, filter);
|
|
||||||
gst_query_set_caps_result (query, caps);
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
res = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res = gst_pad_query_default (pad, parent, query);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_jpegenc_setcaps (GstJpegEnc * enc, GstCaps * caps)
|
|
||||||
{
|
|
||||||
GstVideoInfo info;
|
|
||||||
gint i;
|
gint i;
|
||||||
GstCaps *othercaps;
|
GstVideoInfo *info = &state->info;
|
||||||
gboolean ret;
|
GstVideoCodecState *output;
|
||||||
const GstVideoFormatInfo *vinfo;
|
|
||||||
|
|
||||||
/* get info from caps */
|
if (enc->input_state)
|
||||||
if (!gst_video_info_from_caps (&info, caps))
|
gst_video_codec_state_unref (enc->input_state);
|
||||||
goto refuse_caps;
|
enc->input_state = gst_video_codec_state_ref (state);
|
||||||
|
|
||||||
/* store input description */
|
|
||||||
enc->info = info;
|
|
||||||
|
|
||||||
vinfo = info.finfo;
|
|
||||||
|
|
||||||
/* prepare a cached image description */
|
/* prepare a cached image description */
|
||||||
enc->channels = 3 + (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (vinfo) ? 1 : 0);
|
enc->channels = GST_VIDEO_INFO_N_COMPONENTS (info);
|
||||||
|
|
||||||
/* ... but any alpha is disregarded in encoding */
|
/* ... but any alpha is disregarded in encoding */
|
||||||
if (GST_VIDEO_FORMAT_INFO_IS_GRAY (vinfo))
|
if (GST_VIDEO_INFO_IS_GRAY (info))
|
||||||
enc->channels = 1;
|
enc->channels = 1;
|
||||||
else
|
|
||||||
enc->channels = 3;
|
|
||||||
|
|
||||||
enc->h_max_samp = 0;
|
enc->h_max_samp = 0;
|
||||||
enc->v_max_samp = 0;
|
enc->v_max_samp = 0;
|
||||||
for (i = 0; i < enc->channels; ++i) {
|
for (i = 0; i < enc->channels; ++i) {
|
||||||
enc->cwidth[i] = GST_VIDEO_INFO_COMP_WIDTH (&info, i);
|
enc->cwidth[i] = GST_VIDEO_INFO_COMP_WIDTH (info, i);
|
||||||
enc->cheight[i] = GST_VIDEO_INFO_COMP_HEIGHT (&info, i);
|
enc->cheight[i] = GST_VIDEO_INFO_COMP_HEIGHT (info, i);
|
||||||
enc->inc[i] = GST_VIDEO_INFO_COMP_PSTRIDE (&info, i);
|
enc->inc[i] = GST_VIDEO_INFO_COMP_PSTRIDE (info, i);
|
||||||
|
enc->h_samp[i] =
|
||||||
enc->h_samp[i] = GST_ROUND_UP_4 (info.width) / enc->cwidth[i];
|
GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (info)) / enc->cwidth[i];
|
||||||
enc->h_max_samp = MAX (enc->h_max_samp, enc->h_samp[i]);
|
enc->h_max_samp = MAX (enc->h_max_samp, enc->h_samp[i]);
|
||||||
enc->v_samp[i] = GST_ROUND_UP_4 (info.height) / enc->cheight[i];
|
enc->v_samp[i] =
|
||||||
|
GST_ROUND_UP_4 (GST_VIDEO_INFO_HEIGHT (info)) / enc->cheight[i];
|
||||||
enc->v_max_samp = MAX (enc->v_max_samp, enc->v_samp[i]);
|
enc->v_max_samp = MAX (enc->v_max_samp, enc->v_samp[i]);
|
||||||
}
|
}
|
||||||
/* samp should only be 1, 2 or 4 */
|
/* samp should only be 1, 2 or 4 */
|
||||||
g_assert (enc->h_max_samp <= 4);
|
g_assert (enc->h_max_samp <= 4);
|
||||||
g_assert (enc->v_max_samp <= 4);
|
g_assert (enc->v_max_samp <= 4);
|
||||||
|
|
||||||
/* now invert */
|
/* now invert */
|
||||||
/* maximum is invariant, as one of the components should have samp 1 */
|
/* maximum is invariant, as one of the components should have samp 1 */
|
||||||
for (i = 0; i < enc->channels; ++i) {
|
for (i = 0; i < enc->channels; ++i) {
|
||||||
|
GST_DEBUG ("%d %d", enc->h_samp[i], enc->h_max_samp);
|
||||||
enc->h_samp[i] = enc->h_max_samp / enc->h_samp[i];
|
enc->h_samp[i] = enc->h_max_samp / enc->h_samp[i];
|
||||||
enc->v_samp[i] = enc->v_max_samp / enc->v_samp[i];
|
enc->v_samp[i] = enc->v_max_samp / enc->v_samp[i];
|
||||||
}
|
}
|
||||||
enc->planar = (enc->inc[0] == 1 && enc->inc[1] == 1 && enc->inc[2] == 1);
|
enc->planar = (enc->inc[0] == 1 && enc->inc[1] == 1 && enc->inc[2] == 1);
|
||||||
|
|
||||||
othercaps = gst_caps_copy (gst_pad_get_pad_template_caps (enc->srcpad));
|
output =
|
||||||
gst_caps_set_simple (othercaps,
|
gst_video_encoder_set_output_state (encoder,
|
||||||
"width", G_TYPE_INT, info.width, "height", G_TYPE_INT, info.height, NULL);
|
gst_caps_new_empty_simple ("image/jpeg"), state);
|
||||||
if (info.fps_d > 0)
|
gst_video_codec_state_unref (output);
|
||||||
gst_caps_set_simple (othercaps,
|
|
||||||
"framerate", GST_TYPE_FRACTION, info.fps_n, info.fps_d, NULL);
|
|
||||||
if (info.par_d > 0)
|
|
||||||
gst_caps_set_simple (othercaps,
|
|
||||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, info.par_n, info.par_d, NULL);
|
|
||||||
|
|
||||||
ret = gst_pad_set_caps (enc->srcpad, othercaps);
|
gst_jpegenc_resync (enc);
|
||||||
gst_caps_unref (othercaps);
|
|
||||||
|
|
||||||
if (ret)
|
return TRUE;
|
||||||
gst_jpegenc_resync (enc);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
refuse_caps:
|
|
||||||
{
|
|
||||||
GST_WARNING_OBJECT (enc, "refused caps %" GST_PTR_FORMAT, caps);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_jpegenc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|
||||||
{
|
|
||||||
gboolean res;
|
|
||||||
GstJpegEnc *enc = GST_JPEGENC (parent);
|
|
||||||
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
|
||||||
case GST_EVENT_CAPS:
|
|
||||||
{
|
|
||||||
GstCaps *caps;
|
|
||||||
|
|
||||||
gst_event_parse_caps (event, &caps);
|
|
||||||
res = gst_jpegenc_setcaps (enc, caps);
|
|
||||||
gst_event_unref (event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res = gst_pad_event_default (pad, parent, event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_jpegenc_resync (GstJpegEnc * jpegenc)
|
gst_jpegenc_resync (GstJpegEnc * jpegenc)
|
||||||
{
|
{
|
||||||
|
GstVideoInfo *info;
|
||||||
gint width, height;
|
gint width, height;
|
||||||
gint i, j;
|
gint i, j;
|
||||||
const GstVideoFormatInfo *finfo;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (jpegenc, "resync");
|
GST_DEBUG_OBJECT (jpegenc, "resync");
|
||||||
|
|
||||||
finfo = jpegenc->info.finfo;
|
if (!jpegenc->input_state)
|
||||||
|
return;
|
||||||
|
|
||||||
jpegenc->cinfo.image_width = width = GST_VIDEO_INFO_WIDTH (&jpegenc->info);
|
info = &jpegenc->input_state->info;
|
||||||
jpegenc->cinfo.image_height = height = GST_VIDEO_INFO_HEIGHT (&jpegenc->info);
|
|
||||||
|
jpegenc->cinfo.image_width = width = GST_VIDEO_INFO_WIDTH (info);
|
||||||
|
jpegenc->cinfo.image_height = height = GST_VIDEO_INFO_HEIGHT (info);
|
||||||
jpegenc->cinfo.input_components = jpegenc->channels;
|
jpegenc->cinfo.input_components = jpegenc->channels;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (jpegenc, "width %d, height %d", width, height);
|
GST_DEBUG_OBJECT (jpegenc, "width %d, height %d", width, height);
|
||||||
GST_DEBUG_OBJECT (jpegenc, "format %d",
|
GST_DEBUG_OBJECT (jpegenc, "format %d", GST_VIDEO_INFO_FORMAT (info));
|
||||||
GST_VIDEO_INFO_FORMAT (&jpegenc->info));
|
|
||||||
|
|
||||||
if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo)) {
|
if (GST_VIDEO_INFO_IS_RGB (info)) {
|
||||||
GST_DEBUG_OBJECT (jpegenc, "RGB");
|
GST_DEBUG_OBJECT (jpegenc, "RGB");
|
||||||
jpegenc->cinfo.in_color_space = JCS_RGB;
|
jpegenc->cinfo.in_color_space = JCS_RGB;
|
||||||
} else if (GST_VIDEO_FORMAT_INFO_IS_GRAY (finfo)) {
|
} else if (GST_VIDEO_INFO_IS_GRAY (info)) {
|
||||||
GST_DEBUG_OBJECT (jpegenc, "gray");
|
GST_DEBUG_OBJECT (jpegenc, "gray");
|
||||||
jpegenc->cinfo.in_color_space = JCS_GRAYSCALE;
|
jpegenc->cinfo.in_color_space = JCS_GRAYSCALE;
|
||||||
} else {
|
} else {
|
||||||
|
@ -518,7 +386,7 @@ gst_jpegenc_resync (GstJpegEnc * jpegenc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* input buffer size as max output */
|
/* input buffer size as max output */
|
||||||
jpegenc->bufsize = GST_VIDEO_INFO_SIZE (&jpegenc->info);
|
jpegenc->bufsize = GST_VIDEO_INFO_SIZE (info);
|
||||||
jpeg_set_defaults (&jpegenc->cinfo);
|
jpeg_set_defaults (&jpegenc->cinfo);
|
||||||
jpegenc->cinfo.raw_data_in = TRUE;
|
jpegenc->cinfo.raw_data_in = TRUE;
|
||||||
/* duh, libjpeg maps RGB to YUV ... and don't expect some conversion */
|
/* duh, libjpeg maps RGB to YUV ... and don't expect some conversion */
|
||||||
|
@ -554,36 +422,33 @@ gst_jpegenc_resync (GstJpegEnc * jpegenc)
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_jpegenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
gst_jpegenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame)
|
||||||
{
|
{
|
||||||
GstFlowReturn ret;
|
|
||||||
GstJpegEnc *jpegenc;
|
GstJpegEnc *jpegenc;
|
||||||
guint height;
|
guint height;
|
||||||
guchar *base[3], *end[3];
|
guchar *base[3], *end[3];
|
||||||
guint stride[3];
|
guint stride[3];
|
||||||
gint i, j, k;
|
gint i, j, k;
|
||||||
GstBuffer *outbuf;
|
|
||||||
GstVideoFrame frame;
|
|
||||||
static GstAllocationParams params = { 0, 0, 0, 3, };
|
static GstAllocationParams params = { 0, 0, 0, 3, };
|
||||||
|
|
||||||
jpegenc = GST_JPEGENC (parent);
|
jpegenc = GST_JPEGENC (encoder);
|
||||||
|
|
||||||
if (G_UNLIKELY (GST_VIDEO_INFO_FORMAT (&jpegenc->info) ==
|
GST_LOG_OBJECT (jpegenc, "got new frame");
|
||||||
GST_VIDEO_FORMAT_UNKNOWN))
|
|
||||||
goto not_negotiated;
|
|
||||||
|
|
||||||
if (!gst_video_frame_map (&frame, &jpegenc->info, buf, GST_MAP_READ))
|
if (!gst_video_frame_map (&jpegenc->current_vframe,
|
||||||
|
&jpegenc->input_state->info, frame->input_buffer, GST_MAP_READ))
|
||||||
goto invalid_frame;
|
goto invalid_frame;
|
||||||
|
|
||||||
height = GST_VIDEO_FRAME_HEIGHT (&frame);
|
jpegenc->current_frame = frame;
|
||||||
|
|
||||||
GST_LOG_OBJECT (jpegenc, "got buffer of %" G_GSIZE_FORMAT " bytes",
|
height = GST_VIDEO_INFO_HEIGHT (&jpegenc->input_state->info);
|
||||||
gst_buffer_get_size (buf));
|
|
||||||
|
|
||||||
for (i = 0; i < jpegenc->channels; i++) {
|
for (i = 0; i < jpegenc->channels; i++) {
|
||||||
base[i] = GST_VIDEO_FRAME_COMP_DATA (&frame, i);
|
base[i] = GST_VIDEO_FRAME_COMP_DATA (&jpegenc->current_vframe, i);
|
||||||
stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (&frame, i);
|
stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (&jpegenc->current_vframe, i);
|
||||||
end[i] = base[i] + GST_VIDEO_FRAME_COMP_HEIGHT (&frame, i) * stride[i];
|
end[i] =
|
||||||
|
base[i] + GST_VIDEO_FRAME_COMP_HEIGHT (&jpegenc->current_vframe,
|
||||||
|
i) * stride[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
jpegenc->output_mem = gst_allocator_alloc (NULL, jpegenc->bufsize, ¶ms);
|
jpegenc->output_mem = gst_allocator_alloc (NULL, jpegenc->bufsize, ¶ms);
|
||||||
|
@ -643,33 +508,25 @@ gst_jpegenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
||||||
jpeg_finish_compress (&jpegenc->cinfo);
|
jpeg_finish_compress (&jpegenc->cinfo);
|
||||||
GST_LOG_OBJECT (jpegenc, "compressing done");
|
GST_LOG_OBJECT (jpegenc, "compressing done");
|
||||||
|
|
||||||
outbuf = gst_buffer_new ();
|
return GST_FLOW_OK;
|
||||||
gst_buffer_copy_into (outbuf, buf, GST_BUFFER_COPY_METADATA, 0, -1);
|
|
||||||
gst_buffer_append_memory (outbuf, jpegenc->output_mem);
|
|
||||||
jpegenc->output_mem = NULL;
|
|
||||||
|
|
||||||
ret = gst_pad_push (jpegenc->srcpad, outbuf);
|
|
||||||
|
|
||||||
gst_video_frame_unmap (&frame);
|
|
||||||
gst_buffer_unref (buf);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
not_negotiated:
|
|
||||||
{
|
|
||||||
GST_WARNING_OBJECT (jpegenc, "no input format set (no caps on buffer)");
|
|
||||||
gst_buffer_unref (buf);
|
|
||||||
return GST_FLOW_NOT_NEGOTIATED;
|
|
||||||
}
|
|
||||||
invalid_frame:
|
invalid_frame:
|
||||||
{
|
{
|
||||||
GST_WARNING_OBJECT (jpegenc, "invalid frame received");
|
GST_WARNING_OBJECT (jpegenc, "invalid frame received");
|
||||||
gst_buffer_unref (buf);
|
gst_video_encoder_finish_frame (encoder, frame);
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_jpegenc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
|
||||||
|
{
|
||||||
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE);
|
||||||
|
|
||||||
|
return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
|
||||||
|
query);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_jpegenc_set_property (GObject * object, guint prop_id,
|
gst_jpegenc_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec)
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
@ -726,34 +583,24 @@ gst_jpegenc_get_property (GObject * object, guint prop_id, GValue * value,
|
||||||
GST_OBJECT_UNLOCK (jpegenc);
|
GST_OBJECT_UNLOCK (jpegenc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstStateChangeReturn
|
static gboolean
|
||||||
gst_jpegenc_change_state (GstElement * element, GstStateChange transition)
|
gst_jpegenc_start (GstVideoEncoder * benc)
|
||||||
{
|
{
|
||||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
GstJpegEnc *enc = (GstJpegEnc *) benc;
|
||||||
GstJpegEnc *filter = GST_JPEGENC (element);
|
|
||||||
|
|
||||||
switch (transition) {
|
enc->line[0] = NULL;
|
||||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
enc->line[1] = NULL;
|
||||||
GST_DEBUG_OBJECT (element, "setting line buffers");
|
enc->line[2] = NULL;
|
||||||
filter->line[0] = NULL;
|
|
||||||
filter->line[1] = NULL;
|
|
||||||
filter->line[2] = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
return TRUE;
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
}
|
||||||
return ret;
|
|
||||||
|
static gboolean
|
||||||
switch (transition) {
|
gst_jpegenc_stop (GstVideoEncoder * benc)
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
{
|
||||||
gst_jpegenc_reset (filter);
|
GstJpegEnc *enc = (GstJpegEnc *) benc;
|
||||||
break;
|
|
||||||
default:
|
gst_jpegenc_reset (enc);
|
||||||
break;
|
|
||||||
}
|
return TRUE;
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/* GStreamer
|
/* GStreamer
|
||||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* Copyright (C) 2012 Collabora Ltd.
|
||||||
|
* Author : Edward Hervey <edward@collabora.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
@ -24,6 +26,7 @@
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
#include <gst/video/gstvideoencoder.h>
|
||||||
/* this is a hack hack hack to get around jpeglib header bugs... */
|
/* this is a hack hack hack to get around jpeglib header bugs... */
|
||||||
#ifdef HAVE_STDLIB_H
|
#ifdef HAVE_STDLIB_H
|
||||||
# undef HAVE_STDLIB_H
|
# undef HAVE_STDLIB_H
|
||||||
|
@ -46,23 +49,24 @@ G_BEGIN_DECLS
|
||||||
typedef struct _GstJpegEnc GstJpegEnc;
|
typedef struct _GstJpegEnc GstJpegEnc;
|
||||||
typedef struct _GstJpegEncClass GstJpegEncClass;
|
typedef struct _GstJpegEncClass GstJpegEncClass;
|
||||||
|
|
||||||
|
#define GST_JPEG_ENC_MAX_COMPONENT 4
|
||||||
|
|
||||||
struct _GstJpegEnc
|
struct _GstJpegEnc
|
||||||
{
|
{
|
||||||
GstElement element;
|
GstVideoEncoder encoder;
|
||||||
|
|
||||||
/* pads */
|
GstVideoCodecState *input_state;
|
||||||
GstPad *sinkpad, *srcpad;
|
GstVideoCodecFrame *current_frame;
|
||||||
|
|
||||||
/* stream/image properties */
|
guint channels;
|
||||||
GstVideoInfo info;
|
|
||||||
gint channels;
|
|
||||||
|
|
||||||
/* standard video_format indexed */
|
gint stride[GST_JPEG_ENC_MAX_COMPONENT];
|
||||||
gint inc[GST_VIDEO_MAX_COMPONENTS];
|
gint offset[GST_JPEG_ENC_MAX_COMPONENT];
|
||||||
gint cwidth[GST_VIDEO_MAX_COMPONENTS];
|
gint inc[GST_JPEG_ENC_MAX_COMPONENT];
|
||||||
gint cheight[GST_VIDEO_MAX_COMPONENTS];
|
gint cwidth[GST_JPEG_ENC_MAX_COMPONENT];
|
||||||
gint h_samp[GST_VIDEO_MAX_COMPONENTS];
|
gint cheight[GST_JPEG_ENC_MAX_COMPONENT];
|
||||||
gint v_samp[GST_VIDEO_MAX_COMPONENTS];
|
gint h_samp[GST_JPEG_ENC_MAX_COMPONENT];
|
||||||
|
gint v_samp[GST_JPEG_ENC_MAX_COMPONENT];
|
||||||
gint h_max_samp;
|
gint h_max_samp;
|
||||||
gint v_max_samp;
|
gint v_max_samp;
|
||||||
gboolean planar;
|
gboolean planar;
|
||||||
|
@ -82,16 +86,15 @@ struct _GstJpegEnc
|
||||||
gint smoothing;
|
gint smoothing;
|
||||||
gint idct_method;
|
gint idct_method;
|
||||||
|
|
||||||
/* cached return state for any problems that may occur in callbacks */
|
GstBuffer *output_buffer;
|
||||||
GstFlowReturn last_ret;
|
|
||||||
|
|
||||||
GstMemory *output_mem;
|
|
||||||
GstMapInfo output_map;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstJpegEncClass
|
struct _GstJpegEncClass
|
||||||
{
|
{
|
||||||
GstElementClass parent_class;
|
GstVideoEncoderClass parent_class;
|
||||||
|
|
||||||
|
/* signals */
|
||||||
|
void (*frame_encoded) (GstElement * element);
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gst_jpegenc_get_type (void);
|
GType gst_jpegenc_get_type (void);
|
||||||
|
|
Loading…
Reference in a new issue