| /* GStreamer |
| * Copyright (C) <2005> Wim Taymans <wim@fluendo.com> |
| * |
| * 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 |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #include <string.h> |
| |
| #include <gst/rtp/gstrtpbuffer.h> |
| |
| #include "gstrtph263pay.h" |
| |
| #define GST_RFC2190A_HEADER_LEN 4 |
| |
| typedef struct _GstRFC2190AHeader |
| { |
| #if G_BYTE_ORDER == G_LITTLE_ENDIAN |
| unsigned int ebit:3; /* End position */ |
| unsigned int sbit:3; /* Start position */ |
| unsigned int p:1; /* PB-frames mode */ |
| unsigned int f:1; /* flag bit */ |
| |
| unsigned int r1:1; /* Reserved */ |
| unsigned int a:1; /* Advanced Prediction */ |
| unsigned int s:1; /* syntax based arithmetic coding */ |
| unsigned int u:1; /* Unrestricted motion vector */ |
| unsigned int i:1; /* Picture coding type */ |
| unsigned int src:3; /* Source format */ |
| |
| unsigned int trb:3; /* Temporal ref for B frame */ |
| unsigned int dbq:2; /* Differential Quantisation parameter */ |
| unsigned int r2:3; /* Reserved */ |
| #elif G_BYTE_ORDER == G_BIG_ENDIAN |
| unsigned int f:1; /* flag bit */ |
| unsigned int p:1; /* PB-frames mode */ |
| unsigned int sbit:3; /* Start position */ |
| unsigned int ebit:3; /* End position */ |
| |
| unsigned int src:3; /* Source format */ |
| unsigned int i:1; /* Picture coding type */ |
| unsigned int u:1; /* Unrestricted motion vector */ |
| unsigned int s:1; /* syntax based arithmetic coding */ |
| unsigned int a:1; /* Advanced Prediction */ |
| unsigned int r1:1; /* Reserved */ |
| |
| unsigned int r2:3; /* Reserved */ |
| unsigned int dbq:2; /* Differential Quantisation parameter */ |
| unsigned int trb:3; /* Temporal ref for B frame */ |
| #else |
| #error "G_BYTE_ORDER should be big or little endian." |
| #endif |
| guint8 tr; /* Temporal ref for P frame */ |
| } GstRFC2190AHeader; |
| |
| |
| #define GST_RFC2190A_HEADER_F(buf) (((GstRFC2190AHeader *)(buf))->f) |
| #define GST_RFC2190A_HEADER_P(buf) (((GstRFC2190AHeader *)(buf))->p) |
| #define GST_RFC2190A_HEADER_SBIT(buf) (((GstRFC2190AHeader *)(buf))->sbit) |
| #define GST_RFC2190A_HEADER_EBIT(buf) (((GstRFC2190AHeader *)(buf))->ebit) |
| #define GST_RFC2190A_HEADER_SRC(buf) (((GstRFC2190AHeader *)(buf))->src) |
| #define GST_RFC2190A_HEADER_I(buf) (((GstRFC2190AHeader *)(buf))->i) |
| #define GST_RFC2190A_HEADER_U(buf) (((GstRFC2190AHeader *)(buf))->u) |
| #define GST_RFC2190A_HEADER_S(buf) (((GstRFC2190AHeader *)(buf))->s) |
| #define GST_RFC2190A_HEADER_A(buf) (((GstRFC2190AHeader *)(buf))->a) |
| #define GST_RFC2190A_HEADER_R1(buf) (((GstRFC2190AHeader *)(buf))->r1) |
| #define GST_RFC2190A_HEADER_R2(buf) (((GstRFC2190AHeader *)(buf))->r2) |
| #define GST_RFC2190A_HEADER_DBQ(buf) (((GstRFC2190AHeader *)(buf))->dbq) |
| #define GST_RFC2190A_HEADER_TRB(buf) (((GstRFC2190AHeader *)(buf))->trb) |
| #define GST_RFC2190A_HEADER_TR(buf) (((GstRFC2190AHeader *)(buf))->tr) |
| |
| |
| typedef struct _GstH263PictureLayer |
| { |
| #if G_BYTE_ORDER == G_LITTLE_ENDIAN |
| unsigned int psc1:16; |
| |
| unsigned int tr1:2; |
| unsigned int psc2:6; |
| |
| unsigned int ptype_263:1; |
| unsigned int ptype_start:1; |
| unsigned int tr2:6; |
| |
| unsigned int ptype_umvmode:1; |
| unsigned int ptype_pictype:1; |
| unsigned int ptype_srcformat:3; |
| unsigned int ptype_freeze:1; |
| unsigned int ptype_camera:1; |
| unsigned int ptype_split:1; |
| |
| unsigned int chaff:5; |
| unsigned int ptype_pbmode:1; |
| unsigned int ptype_apmode:1; |
| unsigned int ptype_sacmode:1; |
| #elif G_BYTE_ORDER == G_BIG_ENDIAN |
| unsigned int psc1:16; |
| |
| unsigned int psc2:6; |
| unsigned int tr1:2; |
| |
| unsigned int tr2:6; |
| unsigned int ptype_start:1; |
| unsigned int ptype_263:1; |
| |
| unsigned int ptype_split:1; |
| unsigned int ptype_camera:1; |
| unsigned int ptype_freeze:1; |
| unsigned int ptype_srcformat:3; |
| unsigned int ptype_pictype:1; |
| unsigned int ptype_umvmode:1; |
| |
| unsigned int ptype_sacmode:1; |
| unsigned int ptype_apmode:1; |
| unsigned int ptype_pbmode:1; |
| unsigned int chaff:5; |
| #else |
| #error "G_BYTE_ORDER should be big or little endian." |
| #endif |
| } GstH263PictureLayer; |
| |
| #define GST_H263_PICTURELAYER_PLSRC(buf) (((GstH263PictureLayer *)(buf))->ptype_srcformat) |
| #define GST_H263_PICTURELAYER_PLTYPE(buf) (((GstH263PictureLayer *)(buf))->ptype_pictype) |
| #define GST_H263_PICTURELAYER_PLUMV(buf) (((GstH263PictureLayer *)(buf))->ptype_umvmode) |
| #define GST_H263_PICTURELAYER_PLSAC(buf) (((GstH263PictureLayer *)(buf))->ptype_sacmode) |
| #define GST_H263_PICTURELAYER_PLAP(buf) (((GstH263PictureLayer *)(buf))->ptype_apmode) |
| |
| |
| |
| /* elementfactory information */ |
| static GstElementDetails gst_rtp_h263pay_details = { |
| "RTP packet parser", |
| "Codec/Payloader/Network", |
| "Payload-encodes H263 video in RTP packets (RFC 2190)", |
| "Neil Stratford <neils@vipadia.com>" |
| }; |
| |
| static GstStaticPadTemplate gst_rtp_h263_pay_sink_template = |
| GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("video/x-h263") |
| ); |
| |
| static GstStaticPadTemplate gst_rtp_h263_pay_src_template = |
| GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("application/x-rtp, " |
| "media = (string) \"video\", " |
| "payload = (int) [ 96, 127 ], " |
| "clock-rate = (int) 90000, " "encoding-name = (string) \"H263-1998\"") |
| ); |
| |
| static void gst_rtp_h263_pay_class_init (GstRtpH263PayClass * klass); |
| static void gst_rtp_h263_pay_base_init (GstRtpH263PayClass * klass); |
| static void gst_rtp_h263_pay_init (GstRtpH263Pay * rtph263pay); |
| static void gst_rtp_h263_pay_finalize (GObject * object); |
| |
| static gboolean gst_rtp_h263_pay_setcaps (GstBaseRTPPayload * payload, |
| GstCaps * caps); |
| static GstFlowReturn gst_rtp_h263_pay_handle_buffer (GstBaseRTPPayload * |
| payload, GstBuffer * buffer); |
| |
| static GstBaseRTPPayloadClass *parent_class = NULL; |
| |
| static GType |
| gst_rtp_h263_pay_get_type (void) |
| { |
| static GType rtph263pay_type = 0; |
| |
| if (!rtph263pay_type) { |
| static const GTypeInfo rtph263pay_info = { |
| sizeof (GstRtpH263PayClass), |
| (GBaseInitFunc) gst_rtp_h263_pay_base_init, |
| NULL, |
| (GClassInitFunc) gst_rtp_h263_pay_class_init, |
| NULL, |
| NULL, |
| sizeof (GstRtpH263Pay), |
| 0, |
| (GInstanceInitFunc) gst_rtp_h263_pay_init, |
| }; |
| |
| rtph263pay_type = |
| g_type_register_static (GST_TYPE_BASE_RTP_PAYLOAD, "GstRtpH263Pay", |
| &rtph263pay_info, 0); |
| } |
| return rtph263pay_type; |
| } |
| |
| static void |
| gst_rtp_h263_pay_base_init (GstRtpH263PayClass * klass) |
| { |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
| |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&gst_rtp_h263_pay_src_template)); |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&gst_rtp_h263_pay_sink_template)); |
| |
| gst_element_class_set_details (element_class, &gst_rtp_h263pay_details); |
| } |
| |
| static void |
| gst_rtp_h263_pay_class_init (GstRtpH263PayClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *gstelement_class; |
| GstBaseRTPPayloadClass *gstbasertppayload_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstelement_class = (GstElementClass *) klass; |
| gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass; |
| |
| parent_class = g_type_class_ref (GST_TYPE_BASE_RTP_PAYLOAD); |
| |
| gobject_class->finalize = gst_rtp_h263_pay_finalize; |
| |
| gstbasertppayload_class->set_caps = gst_rtp_h263_pay_setcaps; |
| gstbasertppayload_class->handle_buffer = gst_rtp_h263_pay_handle_buffer; |
| } |
| |
| static void |
| gst_rtp_h263_pay_init (GstRtpH263Pay * rtph263pay) |
| { |
| rtph263pay->adapter = gst_adapter_new (); |
| } |
| |
| static void |
| gst_rtp_h263_pay_finalize (GObject * object) |
| { |
| GstRtpH263Pay *rtph263pay; |
| |
| rtph263pay = GST_RTP_H263_PAY (object); |
| |
| g_object_unref (rtph263pay->adapter); |
| rtph263pay->adapter = NULL; |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| static gboolean |
| gst_rtp_h263_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) |
| { |
| gst_basertppayload_set_options (payload, "video", TRUE, "H263-1998", 90000); |
| gst_basertppayload_set_outcaps (payload, NULL); |
| |
| return TRUE; |
| } |
| |
| static guint |
| gst_rtp_h263_pay_gobfiner (guint8 * data, guint len, guint curpos) |
| { |
| guint16 test = 0xffff; |
| guint i; |
| |
| /* If we are past the end, stop */ |
| if (curpos >= len) |
| return 0; |
| |
| /* start at curpos and loop looking for next gob start */ |
| for (i = curpos; i < len; i++) { |
| test = (test << 8) | data[i]; |
| if ((test == 0) && (i > curpos + 4)) { |
| return i - 3; |
| } |
| } |
| return len; |
| } |
| |
| static GstFlowReturn |
| gst_rtp_h263_pay_flush (GstRtpH263Pay * rtph263pay) |
| { |
| guint avail; |
| GstBuffer *outbuf; |
| GstFlowReturn ret = 0; |
| gboolean fragmented; |
| guint8 *data, *header; |
| GstH263PictureLayer *piclayer; |
| guint8 *payload; |
| guint payload_len, total_len; |
| guint curpos, nextgobpos; |
| |
| avail = gst_adapter_available (rtph263pay->adapter); |
| if (avail == 0) |
| return GST_FLOW_OK; |
| |
| fragmented = FALSE; |
| |
| /* Get a pointer to all the data for the frame */ |
| data = (guint8 *) gst_adapter_peek (rtph263pay->adapter, avail); |
| |
| /* Start at the begining and loop looking for gobs */ |
| curpos = 0; |
| |
| /* Picture header */ |
| piclayer = (GstH263PictureLayer *) data; |
| |
| while ((nextgobpos = gst_rtp_h263_pay_gobfiner (data, avail, curpos)) > 0) { |
| |
| payload_len = nextgobpos - curpos; |
| total_len = payload_len + GST_RFC2190A_HEADER_LEN; |
| |
| outbuf = gst_rtp_buffer_new_allocate (total_len, 0, 0); |
| |
| header = gst_rtp_buffer_get_payload (outbuf); |
| payload = header + GST_RFC2190A_HEADER_LEN; |
| |
| /* Build the headers */ |
| GST_RFC2190A_HEADER_F (header) = 0; |
| GST_RFC2190A_HEADER_P (header) = 0; |
| GST_RFC2190A_HEADER_SBIT (header) = 0; |
| GST_RFC2190A_HEADER_EBIT (header) = 0; |
| |
| GST_RFC2190A_HEADER_SRC (header) = GST_H263_PICTURELAYER_PLSRC (piclayer); |
| GST_RFC2190A_HEADER_I (header) = GST_H263_PICTURELAYER_PLTYPE (piclayer); |
| GST_RFC2190A_HEADER_U (header) = GST_H263_PICTURELAYER_PLUMV (piclayer); |
| GST_RFC2190A_HEADER_S (header) = GST_H263_PICTURELAYER_PLSAC (piclayer); |
| GST_RFC2190A_HEADER_A (header) = GST_H263_PICTURELAYER_PLAP (piclayer); |
| GST_RFC2190A_HEADER_R1 (header) = 0; |
| GST_RFC2190A_HEADER_R2 (header) = 0; |
| GST_RFC2190A_HEADER_DBQ (header) = 0; |
| GST_RFC2190A_HEADER_TRB (header) = 0; |
| GST_RFC2190A_HEADER_TR (header) = 0; |
| |
| /* last fragment gets the marker bit set */ |
| gst_rtp_buffer_set_marker (outbuf, nextgobpos < avail ? 0 : 1); |
| |
| memcpy (payload, data + curpos, payload_len); |
| |
| GST_BUFFER_TIMESTAMP (outbuf) = rtph263pay->first_ts; |
| |
| ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtph263pay), outbuf); |
| |
| curpos = nextgobpos; |
| } |
| |
| /* Flush the whole packet */ |
| gst_adapter_flush (rtph263pay->adapter, avail); |
| |
| return ret; |
| } |
| |
| static GstFlowReturn |
| gst_rtp_h263_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer) |
| { |
| GstRtpH263Pay *rtph263pay; |
| GstFlowReturn ret; |
| guint size; |
| |
| rtph263pay = GST_RTP_H263_PAY (payload); |
| |
| size = GST_BUFFER_SIZE (buffer); |
| rtph263pay->first_ts = GST_BUFFER_TIMESTAMP (buffer); |
| |
| /* we always encode and flush a full picture */ |
| gst_adapter_push (rtph263pay->adapter, buffer); |
| ret = gst_rtp_h263_pay_flush (rtph263pay); |
| |
| return ret; |
| } |
| |
| gboolean |
| gst_rtp_h263_pay_plugin_init (GstPlugin * plugin) |
| { |
| return gst_element_register (plugin, "rtph263pay", |
| GST_RANK_NONE, GST_TYPE_RTP_H263_PAY); |
| } |