gstreamer/gst/rtp/gstrtpamrdepay.c
Philippe Kalaf 9a6ca70be2 gst-plugins-good/gst/rtp/: Fixed payload range in payloder caps. Removed payload range completly from depayloaders as...
Original commit message from CVS:
2005-12-14  Philippe Khalaf  <burger@speedy.org>

* gst-plugins-good/gst/rtp/gstasteriskh263.c:
* gst-plugins-good/gst/rtp/gstrtpamrdepay.c:
* gst-plugins-good/gst/rtp/gstrtpamrpay.c:
* gst-plugins-good/gst/rtp/gstrtpg711depay.c:
* gst-plugins-good/gst/rtp/gstrtpg711depay.c:
* gst-plugins-good/gst/rtp/gstrtpgsmdepay.c:
* gst-plugins-good/gst/rtp/gstrtph263pay.c:
* gst-plugins-good/gst/rtp/gstrtph263pdepay.c:
* gst-plugins-good/gst/rtp/gstrtph263ppay.c:
* gst-plugins-good/gst/rtp/gstrtpmp4vdepay.c:
* gst-plugins-good/gst/rtp/gstrtpmp4vpay.c:
* gst-plugins-good/gst/rtp/gstrtpmpadepay.c:
* gst-plugins-good/gst/rtp/gstrtpmpapay.c:
* gst-plugins-good/gst/rtp/README:
Fixed payload range in payloder caps. Removed payload range completly from
depayloaders as they don't require payload type in their caps. In effect,
there isn't any specific payload type for any given codec, only suggestions.
Fixes bug #324011.
2005-12-14 18:07:16 +00:00

486 lines
13 KiB
C

/* 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 <gst/rtp/gstrtpbuffer.h>
#include <string.h>
#include "gstrtpamrdepay.h"
/* references:
*
* RFC 3267 - Real-Time Transport Protocol (RTP) Payload Format and File Storage Format
* for the Adaptive Multi-Rate (AMR) and Adaptive Multi-Rate Wideband (AMR-WB) Audio
* Codecs.
*/
/* elementfactory information */
static GstElementDetails gst_rtp_amrdepay_details = {
"RTP packet parser",
"Codec/Depayr/Network",
"Extracts AMR audio from RTP packets (RFC 3267)",
"Wim Taymans <wim@fluendo.com>"
};
/* RtpAMRDepay signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_0,
ARG_FREQUENCY
};
/* input is an RTP packet
*
* params see RFC 3267, section 8.1
*/
static GstStaticPadTemplate gst_rtp_amr_depay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp, "
"media = (string) \"audio\", "
"clock-rate = (int) 8000, "
"encoding-name = (string) \"AMR\", "
"encoding-params = (string) \"1\", "
"octet-align = (string) \"1\", "
"crc = (string) { \"0\", \"1\" }, "
"robust-sorting = (string) \"0\", " "interleaving = (string) \"0\""
/* following options are not needed for a decoder
*
"mode-set = (int) [ 0, 7 ], "
"mode-change-period = (int) [ 1, MAX ], "
"mode-change-neighbor = (boolean) { TRUE, FALSE }, "
"maxptime = (int) [ 20, MAX ], "
"ptime = (int) [ 20, MAX ]"
*/
)
);
static GstStaticPadTemplate gst_rtp_amr_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/AMR, " "channels = (int) 1," "rate = (int) 8000")
);
static void gst_rtp_amr_depay_class_init (GstRtpAMRDepayClass * klass);
static void gst_rtp_amr_depay_base_init (GstRtpAMRDepayClass * klass);
static void gst_rtp_amr_depay_init (GstRtpAMRDepay * rtpamrdepay);
static gboolean gst_rtp_amr_depay_sink_setcaps (GstPad * pad, GstCaps * caps);
static GstFlowReturn gst_rtp_amr_depay_chain (GstPad * pad, GstBuffer * buffer);
static void gst_rtp_amr_depay_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_rtp_amr_depay_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstStateChangeReturn gst_rtp_amr_depay_change_state (GstElement *
element, GstStateChange transition);
static GstElementClass *parent_class = NULL;
static GType
gst_rtp_amr_depay_get_type (void)
{
static GType rtpamrdepay_type = 0;
if (!rtpamrdepay_type) {
static const GTypeInfo rtpamrdepay_info = {
sizeof (GstRtpAMRDepayClass),
(GBaseInitFunc) gst_rtp_amr_depay_base_init,
NULL,
(GClassInitFunc) gst_rtp_amr_depay_class_init,
NULL,
NULL,
sizeof (GstRtpAMRDepay),
0,
(GInstanceInitFunc) gst_rtp_amr_depay_init,
};
rtpamrdepay_type =
g_type_register_static (GST_TYPE_ELEMENT, "GstRtpAMRDepay",
&rtpamrdepay_info, 0);
}
return rtpamrdepay_type;
}
static void
gst_rtp_amr_depay_base_init (GstRtpAMRDepayClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_amr_depay_src_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_amr_depay_sink_template));
gst_element_class_set_details (element_class, &gst_rtp_amrdepay_details);
}
static void
gst_rtp_amr_depay_class_init (GstRtpAMRDepayClass * 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_rtp_amr_depay_set_property;
gobject_class->get_property = gst_rtp_amr_depay_get_property;
gstelement_class->change_state = gst_rtp_amr_depay_change_state;
}
static void
gst_rtp_amr_depay_init (GstRtpAMRDepay * rtpamrdepay)
{
rtpamrdepay->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get
(&gst_rtp_amr_depay_src_template), "src");
gst_element_add_pad (GST_ELEMENT (rtpamrdepay), rtpamrdepay->srcpad);
rtpamrdepay->sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get
(&gst_rtp_amr_depay_sink_template), "sink");
gst_pad_set_setcaps_function (rtpamrdepay->sinkpad,
gst_rtp_amr_depay_sink_setcaps);
gst_pad_set_chain_function (rtpamrdepay->sinkpad, gst_rtp_amr_depay_chain);
gst_element_add_pad (GST_ELEMENT (rtpamrdepay), rtpamrdepay->sinkpad);
}
static gboolean
gst_rtp_amr_depay_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstStructure *structure;
GstCaps *srccaps;
GstRtpAMRDepay *rtpamrdepay;
const gchar *params;
const gchar *str;
rtpamrdepay = GST_RTP_AMR_DEPAY (GST_OBJECT_PARENT (pad));
structure = gst_caps_get_structure (caps, 0);
if (!(str = gst_structure_get_string (structure, "octet-align")))
rtpamrdepay->octet_align = FALSE;
else
rtpamrdepay->octet_align = (atoi (str) == 1);
if (!(str = gst_structure_get_string (structure, "crc")))
rtpamrdepay->crc = FALSE;
else
rtpamrdepay->crc = (atoi (str) == 1);
if (rtpamrdepay->crc) {
/* crc mode implies octet aligned mode */
rtpamrdepay->octet_align = TRUE;
}
if (!(str = gst_structure_get_string (structure, "robust-sorting")))
rtpamrdepay->robust_sorting = FALSE;
else
rtpamrdepay->robust_sorting = (atoi (str) == 1);
if (rtpamrdepay->robust_sorting) {
/* robust_sorting mode implies octet aligned mode */
rtpamrdepay->octet_align = TRUE;
}
if (!(str = gst_structure_get_string (structure, "interleaving")))
rtpamrdepay->interleaving = FALSE;
else
rtpamrdepay->interleaving = (atoi (str) == 1);
if (rtpamrdepay->interleaving) {
/* interleaving mode implies octet aligned mode */
rtpamrdepay->octet_align = TRUE;
}
if (!(params = gst_structure_get_string (structure, "encoding-params")))
rtpamrdepay->channels = 1;
else {
rtpamrdepay->channels = atoi (params);
}
if (!gst_structure_get_int (structure, "clock-rate", &rtpamrdepay->rate))
rtpamrdepay->rate = 8000;
/* we require 1 channel, 8000 Hz, octet aligned, no CRC,
* no robust sorting, no interleaving for now */
if (rtpamrdepay->channels != 1)
return FALSE;
if (rtpamrdepay->rate != 8000)
return FALSE;
if (rtpamrdepay->octet_align != TRUE)
return FALSE;
if (rtpamrdepay->robust_sorting != FALSE)
return FALSE;
if (rtpamrdepay->interleaving != FALSE)
return FALSE;
srccaps = gst_caps_new_simple ("audio/AMR",
"channels", G_TYPE_INT, rtpamrdepay->channels,
"rate", G_TYPE_INT, rtpamrdepay->rate, NULL);
gst_pad_set_caps (rtpamrdepay->srcpad, srccaps);
gst_caps_unref (srccaps);
rtpamrdepay->negotiated = TRUE;
return TRUE;
}
/* -1 is invalid */
static gint frame_size[16] = {
12, 13, 15, 17, 19, 20, 26, 31,
5, -1, -1, -1, -1, -1, -1, 0
};
static GstFlowReturn
gst_rtp_amr_depay_chain (GstPad * pad, GstBuffer * buf)
{
GstRtpAMRDepay *rtpamrdepay;
GstBuffer *outbuf;
GstFlowReturn ret;
rtpamrdepay = GST_RTP_AMR_DEPAY (GST_OBJECT_PARENT (pad));
if (!rtpamrdepay->negotiated)
goto not_negotiated;
if (!gst_rtp_buffer_validate (buf))
goto bad_packet;
/* when we get here, 1 channel, 8000 Hz, octet aligned, no CRC,
* no robust sorting, no interleaving data is to be depayd */
{
gint payload_len;
guint8 *payload, *p, *dp;
guint32 timestamp;
guint8 CMR;
gint i, num_packets, num_nonempty_packets;
gint amr_len;
gint ILL, ILP;
payload_len = gst_rtp_buffer_get_payload_len (buf);
/* need at least 2 bytes for the header */
if (payload_len < 2)
goto bad_packet;
payload = gst_rtp_buffer_get_payload (buf);
/* depay CMR. The CMR is used by the sender to request
* a new encoding mode.
*
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
* | CMR |R|R|R|R|
* +-+-+-+-+-+-+-+-+
*/
CMR = (payload[0] & 0xf0) >> 4;
/* strip CMR header now, pack FT and the data for the decoder */
payload_len -= 1;
payload += 1;
if (rtpamrdepay->interleaving) {
ILL = (payload[0] & 0xf0) >> 4;
ILP = (payload[0] & 0x0f);
payload_len -= 1;
payload += 1;
if (ILP > ILL)
goto bad_packet;
}
/*
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
* +-+-+-+-+-+-+-+-+..
* |F| FT |Q|P|P| more FT..
* +-+-+-+-+-+-+-+-+..
*/
/* count number of packets by counting the FTs. Also
* count number of amr data bytes and number of non-empty
* packets (this is also the number of CRCs if present). */
amr_len = 0;
num_nonempty_packets = 0;
num_packets = 0;
for (i = 0; i < payload_len; i++) {
gint fr_size;
guint8 FT;
FT = (payload[i] & 0x78) >> 3;
fr_size = frame_size[FT];
if (fr_size == -1)
goto bad_packet;
if (fr_size > 0) {
amr_len += fr_size;
num_nonempty_packets++;
}
num_packets++;
if ((payload[i] & 0x80) == 0)
break;
}
/* this is impossible */
if (num_packets == payload_len)
goto bad_packet;
if (rtpamrdepay->crc) {
/* data len + CRC len + header bytes should be smaller than payload_len */
if (num_packets + num_nonempty_packets + amr_len > payload_len)
goto bad_packet;
} else {
/* data len + header bytes should be smaller than payload_len */
if (num_packets + amr_len > payload_len)
goto bad_packet;
}
timestamp = gst_rtp_buffer_get_timestamp (buf);
outbuf = gst_buffer_new_and_alloc (payload_len);
GST_BUFFER_TIMESTAMP (outbuf) = timestamp * GST_SECOND / rtpamrdepay->rate;
/* point to destination */
p = GST_BUFFER_DATA (outbuf);
/* point to first data packet */
dp = payload + num_packets;
if (rtpamrdepay->crc) {
/* skip CRC if present */
dp += num_nonempty_packets;
}
for (i = 0; i < num_packets; i++) {
gint fr_size;
fr_size = frame_size[(payload[i] & 0x78) >> 3];
if (fr_size > 0) {
/* copy FT */
*p++ = payload[i];
/* copy data packet, FIXME, calc CRC here. */
memcpy (p, dp, fr_size);
p += fr_size;
dp += fr_size;
}
}
gst_buffer_set_caps (outbuf, GST_PAD_CAPS (rtpamrdepay->srcpad));
GST_DEBUG ("gst_rtp_amr_depay_chain: pushing buffer of size %d",
GST_BUFFER_SIZE (outbuf));
ret = gst_pad_push (rtpamrdepay->srcpad, outbuf);
gst_buffer_unref (buf);
}
return ret;
not_negotiated:
{
GST_ELEMENT_ERROR (rtpamrdepay, STREAM, NOT_IMPLEMENTED,
("not negotiated"), (NULL));
gst_buffer_unref (buf);
return GST_FLOW_NOT_NEGOTIATED;
}
bad_packet:
{
GST_ELEMENT_WARNING (rtpamrdepay, STREAM, DECODE,
("amr packet did not validate"), (NULL));
gst_buffer_unref (buf);
return GST_FLOW_OK;
}
}
static void
gst_rtp_amr_depay_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstRtpAMRDepay *rtpamrdepay;
rtpamrdepay = GST_RTP_AMR_DEPAY (object);
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_rtp_amr_depay_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstRtpAMRDepay *rtpamrdepay;
rtpamrdepay = GST_RTP_AMR_DEPAY (object);
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GstStateChangeReturn
gst_rtp_amr_depay_change_state (GstElement * element, GstStateChange transition)
{
GstRtpAMRDepay *rtpamrdepay;
GstStateChangeReturn ret;
rtpamrdepay = GST_RTP_AMR_DEPAY (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_NULL:
break;
default:
break;
}
return ret;
}
gboolean
gst_rtp_amr_depay_plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "rtpamrdepay",
GST_RANK_NONE, GST_TYPE_RTP_AMR_DEPAY);
}