include #include #include 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 , Philippe Khalaf " }; 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; ibits.rtp_count-1; i++) for(i=1; ibits.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; } }