rendered paste bodyinclude <gst/gst.h>
#include <gstr263enc.h>
#include <gst/rtp/gstrtpbuffer.h>
GST_DEBUG_CATEGORY (r263enc_debug);
#define GST_CAT_DEFAULT (r263enc_debug)
/* Filter signals and args */
enum {
/* FILL ME */
LAST_SIGNAL
};
enum {
ARG_0,
ARG_SILENT,
ARG_RTP,
};
static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
"sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-raw-yuv, "
"format = (fourcc) I420, "
"width = (int) [176, 4096], "
"height = (int) [144, 4096], "
"framerate = (double) [1, 30]" )
);
static GstStaticPadTemplate src_factory =
GST_STATIC_PAD_TEMPLATE (
"src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
// GST_STATIC_CAPS ("video/x-h263; application/x-rtp-noheader")
GST_STATIC_CAPS("ANY")
);
static void gst_r263enc_class_init (GstR263EncClass *klass);
static void gst_r263enc_base_init (GstR263EncClass *klass);
static void gst_r263enc_init (GstR263Enc *filter);
static GstElementStateReturn gst_r263enc_change_state (GstElement * element);
static void gst_r263enc_finalize (GObject * object);
static void gst_r263enc_set_property (GObject *object, guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gst_r263enc_get_property (GObject *object, guint prop_id,
GValue *value,
GParamSpec *pspec);
static GstFlowReturn gst_r263enc_chain (GstPad *pad, GstBuffer *in);
static gboolean gst_r263enc_setcaps (GstPad * pad, GstCaps * caps);
static GstElementClass *parent_class = NULL;
/**********************************************
* libr263 functions begin here *
**********************************************/
static void uninit_encoder(GstR263Enc *enc)
{
if (!enc->enc_inited) return;
CloseCompress(&enc->cparams); /* Use standard compression parameters */
g_free(enc->src_int_frame);
g_free(enc->EncodeBlocks);
enc->enc_inited = FALSE;
enc->is_qcif = FALSE;
}
static void init_encoder(GstR263Enc *enc)
{
if (enc->enc_inited)
{
uninit_encoder(enc);
}
if (enc->width==QCIF_YWIDTH && enc->height==QCIF_YHEIGHT)
{
enc->is_qcif = TRUE;
enc->src_int_frame = g_malloc(sizeof(struct qcif));
enc->cparams.format = CPARAM_QCIF;
}
else if (enc->width==CIF_YWIDTH && enc->height==CIF_YHEIGHT)
{
enc->src_int_frame = g_malloc(sizeof(struct cif));
enc->cparams.format = CPARAM_CIF;
}
else if (enc->width==SQCIF_YWIDTH && enc->height==SQCIF_YHEIGHT)
{
enc->src_int_frame = g_malloc(sizeof(int)*SQCIF_YWIDTH*SQCIF_YHEIGHT*3/2);
enc->cparams.format = CPARAM_SQCIF;
}
else if (enc->width==CIF4_YWIDTH && enc->height==CIF4_YHEIGHT)
{
enc->src_int_frame = g_malloc(sizeof(int)*CIF4_YWIDTH*CIF4_YHEIGHT*3/2);
enc->cparams.format = CPARAM_4CIF;
}
else if (enc->width==CIF16_YWIDTH && enc->height==CIF16_YHEIGHT)
{
enc->src_int_frame = g_malloc(sizeof(int)*CIF16_YWIDTH*CIF16_YHEIGHT*3/2);
enc->cparams.format = CPARAM_16CIF;
}
else
{
GST_DEBUG("RUNNING INIT %d %d", enc->width, enc->height);
enc->cparams.format = CPARAM_OTHER;
enc->cparams.pels = enc->width;
enc->cparams.lines = enc->height;
enc->src_int_frame = g_malloc(sizeof(int)*enc->width*enc->height*3/2);
}
enc->EncodeBlocks = g_malloc0(sizeof(gint)*enc->width*enc->height/16/16);
InitCompress(&enc->cparams); /* Use standard compression parameters */
enc->cparams.Q_intra = 8;
enc->cparams.half_pixel_searchwindow = 4;
enc->cparams.search_method = CPARAM_LOGARITHMIC;
enc->cparams.Q_inter = 8;
enc->cparams.advanced_method = CPARAM_ADVANCED;
enc->cparams.EncodeThisBlock = (int *)enc->EncodeBlocks;
enc->gop_length = 32;
enc->cparams.rtp_threshold = enc->rtp_support? 850 : 0;
enc->cparams.data = (unsigned int *)enc->src_int_frame;
stream_ptr = 0;
enc->frame_no = 0;
enc->enc_inited = TRUE;
}
/**********************************************
* libr263 functions end here *
**********************************************/
GType
gst_r263enc_get_type (void)
{
static GType plugin_type = 0;
if (!plugin_type)
{
static const GTypeInfo plugin_info =
{
sizeof (GstR263EncClass),
(GBaseInitFunc) gst_r263enc_base_init,
NULL,
(GClassInitFunc) gst_r263enc_class_init,
NULL,
NULL,
sizeof (GstR263Enc),
0,
(GInstanceInitFunc) gst_r263enc_init,
};
plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
"GstR263Enc",
&plugin_info, 0);
}
return plugin_type;
}
static gboolean gst_r263enc_setcaps (GstPad * pad, GstCaps * caps)
{
GstStructure *structure = gst_caps_get_structure (caps, 0);
GstR263Enc *filter;
filter = GST_R263ENC (gst_pad_get_parent (pad));
gboolean ret;
#if 0
struct fourcc_list_struct *fourcc;
fourcc = (struct fourcc_list_struct *)paintinfo_find_by_structure (structure);
if (!fourcc) {
g_critical ("videotestsrc format not found\n");
return GST_PAD_LINK_REFUSED;
}
#endif
ret = gst_structure_get_int (structure, "width", &filter->width);
ret &= gst_structure_get_int (structure, "height", &filter->height);
ret &= gst_structure_get_double (structure, "framerate", &filter->framerate);
if (!ret)
return FALSE;
//if (filter->width % 16 || filter->height % 16)
// return FALSE;
GST_DEBUG ("got size %d x %d, initing compression lib", filter->width, filter->height);
init_encoder(filter);
return TRUE;
}
static void
gst_r263enc_base_init (GstR263EncClass *klass)
{
static GstElementDetails plugin_details = {
"h.263 video encoder",
"Codec/Encoder/Video",
"Using libr263 by Roalt Aalmoes, http://www.huygens.org",
"Zhanglei Wang <filamoon@hotmail.com>, Philippe Khalaf <burger@speedy.org>"
};
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_set_details (element_class, &plugin_details);
}
/* initialize the plugin's class */
static void
gst_r263enc_class_init (GstR263EncClass *klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass*) klass;
gstelement_class = (GstElementClass*) klass;
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
gobject_class->set_property = gst_r263enc_set_property;
gobject_class->get_property = gst_r263enc_get_property;
g_object_class_install_property (gobject_class, ARG_SILENT,
g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
FALSE, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_RTP,
g_param_spec_boolean ("rtp_support", "rtp support", "Turn on RTP support ?",
FALSE, G_PARAM_READWRITE));
//gstelement_class->change_state = gst_r263enc_change_state;
gobject_class->finalize = gst_r263enc_finalize;
GST_DEBUG_CATEGORY_INIT (r263enc_debug, "r263enc", 0, "R263 Encoder and RTP payloader");
}
/* initialize the new element
* instantiate pads and add them to element
* set functions
* initialize structure
*/
static void
gst_r263enc_init (GstR263Enc *filter)
{
GstElementClass *klass = GST_ELEMENT_GET_CLASS (filter);
filter->sinkpad = gst_pad_new_from_template (
gst_element_class_get_pad_template (klass, "sink"), "sink");
gst_pad_set_setcaps_function (filter->sinkpad, gst_r263enc_setcaps);
gst_pad_set_getcaps_function (filter->sinkpad, gst_pad_proxy_getcaps);
filter->srcpad = gst_pad_new_from_template (
gst_element_class_get_pad_template (klass, "src"), "src");
gst_pad_set_setcaps_function (filter->srcpad, gst_pad_proxy_setcaps);
gst_pad_set_getcaps_function (filter->srcpad, gst_pad_proxy_getcaps);
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
gst_pad_set_chain_function (filter->sinkpad, gst_r263enc_chain);
filter->silent = FALSE;
filter->rtp_support = FALSE;
filter->enc_inited = FALSE;
filter->is_qcif = FALSE;
}
static void gst_r263enc_finalize (GObject * object)
{
GstR263Enc *filter;
g_return_if_fail (GST_IS_R263ENC (object));
filter = GST_R263ENC (object);
uninit_encoder (filter);
if (G_OBJECT_CLASS (parent_class)->finalize)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/* chain function
* this function does the actual processing
*/
static GstFlowReturn
gst_r263enc_chain (GstPad *pad, GstBuffer *in)
{
GstR263Enc *r;
GstBuffer *out_buf;
GstRTPBuffer *rtp_buf = NULL;
GstCaps *caps;
gint offset;
gint header_int;
gchar * header = (gchar*) &header_int;
gint i;
static gboolean first = TRUE;
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (GST_BUFFER_DATA(in) != NULL);
r = GST_R263ENC (GST_OBJECT_PARENT (pad));
g_return_if_fail (GST_IS_R263ENC (r));
if (!r->enc_inited)
{
GST_DEBUG("Encoder not initiated! exiting chain");
return;
}
if (r->is_qcif)
ReadQCIFBuffer((const unsigned char*)GST_BUFFER_DATA(in), r->src_int_frame);
else
ReadBuffer((const unsigned char*)GST_BUFFER_DATA(in), r->src_int_frame, r->width, r->height);
out_buf = gst_buffer_new_and_alloc(1024*40);
stream_buffer = GST_BUFFER_DATA(out_buf);
stream_ptr = r->rtp_support? 4 : 0;
if ( ( (r->frame_no++) % r->gop_length) == 0 )
{
r->cparams.inter = CPARAM_INTRA;
CompressToH263(&r->cparams, &r->bits);
}
else
{
r->cparams.inter = CPARAM_INTER;
if(r->cparams.advanced_method)
FindMotion(&r->cparams,2,2);
CompressToH263(&r->cparams, &r->bits);
}
GST_BUFFER_SIZE(out_buf) = stream_ptr;
/* payload header */
if (r->rtp_support)
{
header[0] = 0;
/* header[0] |= (sbit << 3 | ebit); */ /* always byte-aligned */
//1: SRC(3) I U S A R
header[1] = 0x40 | ( (r->cparams.inter == CPARAM_INTER) << 4 ); //SRC=010, QCIF
//2: R R R DBQ(2) TRB(3)
header[2] = 0; //R must be 0, we don't use B frames, so DBQ=00, TRB=000
//3: TR(8)
header[3] = (char) r->frame_no; //(((int64_t)s->picture_number * 30 * s->avctx->frame_rate_base) /
GST_DEBUG("Got frame size is %d, rtp_count is : %d, rtp_offsetlast is : %d, frame : %d\n",
stream_ptr, r->bits.rtp_count, r->bits.rtp_offset[r->bits.rtp_count-1],
r->frame_no);
offset = 0;
//for(i=1; i<r->bits.rtp_count-1; i++)
for(i=1; i<r->bits.rtp_count; i++)
{
*(int*)(stream_buffer+offset) = header_int;
rtp_buf = gst_rtpbuffer_new();
if (first)
{
GST_DEBUG("Setting caps on first buffer");
gst_buffer_set_caps (GST_BUFFER(rtp_buf), gst_pad_get_caps(r->srcpad));
first = FALSE;
}
rtp_buf->pt = 34;
rtp_buf->mark = FALSE;
// clock rate is 90 kHz for this payload
rtp_buf->timestampinc = 90000 / r->framerate;
int size = r->bits.rtp_offset[i] - r->bits.rtp_offset[i-1];
GST_DEBUG("%d : One part is %d, from %d %d %d", i, size, offset, r->bits.rtp_offset[i-1], r->bits.rtp_offset[i]);
GST_BUFFER(rtp_buf)->malloc_data = (guint8*)g_malloc (size);
GST_BUFFER_DATA (rtp_buf) = GST_BUFFER(rtp_buf)->malloc_data;
GST_BUFFER_SIZE(rtp_buf) = size;
memcpy (GST_BUFFER_DATA(rtp_buf), GST_BUFFER_DATA(out_buf)+offset, size);
if(i+1 >= r->bits.rtp_count)
{
rtp_buf->mark = TRUE;
}
gst_pad_push (r->srcpad, GST_BUFFER(rtp_buf));
offset = r->bits.rtp_offset[i];
}
gst_buffer_unref(out_buf);
}
else
{
if (first)
{
GST_DEBUG("Setting caps on first buffer");
gst_buffer_set_caps (out_buf, gst_pad_get_caps(r->srcpad));
first = FALSE;
}
gst_pad_push (r->srcpad, GST_BUFFER (out_buf));
}
gst_buffer_unref(in);
}
/*
static GstElementStateReturn
gst_r263enc_change_state (GstElement * element)
{
GST_DEBUG("custom change state");
GstR263Enc *filter;
g_return_if_fail (GST_IS_R263ENC (element));
filter = GST_R263ENC (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_NULL_TO_READY:
break;
case GST_STATE_READY_TO_PAUSED:
init_encoder(filter);
break;
case GST_STATE_PAUSED_TO_PLAYING:
init_encoder(filter);
break;
case GST_STATE_PLAYING_TO_PAUSED:
uninit_encoder(filter);
break;
case GST_STATE_PAUSED_TO_READY:
break;
case GST_STATE_READY_TO_NULL:
break;
}
return parent_class->change_state (element);
}*/
static void
gst_r263enc_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstR263Enc *filter;
g_return_if_fail (GST_IS_R263ENC (object));
filter = GST_R263ENC (object);
switch (prop_id)
{
case ARG_SILENT:
filter->silent = g_value_get_boolean (value);
break;
case ARG_RTP:
filter->rtp_support = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_r263enc_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstR263Enc *filter;
g_return_if_fail (GST_IS_R263ENC (object));
filter = GST_R263ENC (object);
switch (prop_id) {
case ARG_SILENT:
g_value_set_boolean (value, filter->silent);
break;
case ARG_RTP:
g_value_set_boolean (value, filter->rtp_support);
GstCaps *caps = NULL;
if (filter->rtp_support)
{
caps = gst_caps_new_simple ("application/x-rtp-noheader", NULL);
}
else
{
caps = gst_caps_new_simple ("video/x-h263", NULL);
}
GST_DEBUG("setting caps on pad");
gst_pad_set_caps(filter->srcpad, caps);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}