gstrtpspeexpay.c 9.36 KB
Newer Older
1
/* GStreamer
Edgard Lima's avatar
Edgard Lima committed
2
 * Copyright (C) <2005> Edgard Lima <edgard.lima@gmail.com>
3 4 5 6 7 8 9 10 11
 *
 * 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
12 13 14 15
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
16 17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
18 19 20 21 22 23 24 25 26
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <stdlib.h>
#include <string.h>
#include <gst/rtp/gstrtpbuffer.h>
27
#include <gst/audio/audio.h>
28

29
#include "gstrtpspeexpay.h"
30
#include "gstrtputils.h"
31

32 33 34
GST_DEBUG_CATEGORY_STATIC (rtpspeexpay_debug);
#define GST_CAT_DEFAULT (rtpspeexpay_debug)

35
static GstStaticPadTemplate gst_rtp_speex_pay_sink_template =
36 37 38
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
39 40
    GST_STATIC_CAPS ("audio/x-speex, "
        "rate = (int) [ 6000, 48000 ], " "channels = (int) 1")
41 42
    );

43
static GstStaticPadTemplate gst_rtp_speex_pay_src_template =
44 45 46
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
47 48
    GST_STATIC_CAPS ("application/x-rtp, "
        "media = (string) \"audio\", "
49
        "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
50
        "clock-rate =  (int) [ 6000, 48000 ], "
51
        "encoding-name = (string) \"SPEEX\", "
52 53 54
        "encoding-params = (string) \"1\"")
    );

55 56 57
static GstStateChangeReturn gst_rtp_speex_pay_change_state (GstElement *
    element, GstStateChange transition);

58
static gboolean gst_rtp_speex_pay_setcaps (GstRTPBasePayload * payload,
59
    GstCaps * caps);
60
static GstCaps *gst_rtp_speex_pay_getcaps (GstRTPBasePayload * payload,
61
    GstPad * pad, GstCaps * filter);
62
static GstFlowReturn gst_rtp_speex_pay_handle_buffer (GstRTPBasePayload *
63
    payload, GstBuffer * buffer);
64

65
#define gst_rtp_speex_pay_parent_class parent_class
66
G_DEFINE_TYPE (GstRtpSPEEXPay, gst_rtp_speex_pay, GST_TYPE_RTP_BASE_PAYLOAD);
67 68

static void
69
gst_rtp_speex_pay_class_init (GstRtpSPEEXPayClass * klass)
70 71
{
  GstElementClass *gstelement_class;
72
  GstRTPBasePayloadClass *gstrtpbasepayload_class;
73 74

  gstelement_class = (GstElementClass *) klass;
75
  gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
76

77
  gstelement_class->change_state = gst_rtp_speex_pay_change_state;
78

79 80 81
  gstrtpbasepayload_class->set_caps = gst_rtp_speex_pay_setcaps;
  gstrtpbasepayload_class->get_caps = gst_rtp_speex_pay_getcaps;
  gstrtpbasepayload_class->handle_buffer = gst_rtp_speex_pay_handle_buffer;
82

83 84 85 86
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_rtp_speex_pay_sink_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_rtp_speex_pay_src_template);
87 88
  gst_element_class_set_static_metadata (gstelement_class,
      "RTP Speex payloader", "Codec/Payloader/Network/RTP",
89
      "Payload-encodes Speex audio into a RTP packet",
Edgard Lima's avatar
Edgard Lima committed
90
      "Edgard Lima <edgard.lima@gmail.com>");
91 92 93

  GST_DEBUG_CATEGORY_INIT (rtpspeexpay_debug, "rtpspeexpay", 0,
      "Speex RTP Payloader");
94 95 96
}

static void
97
gst_rtp_speex_pay_init (GstRtpSPEEXPay * rtpspeexpay)
98
{
99 100
  GST_RTP_BASE_PAYLOAD (rtpspeexpay)->clock_rate = 8000;
  GST_RTP_BASE_PAYLOAD_PT (rtpspeexpay) = 110;  /* Create String */
101 102 103
}

static gboolean
104
gst_rtp_speex_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
105
{
106 107 108 109
  /* don't configure yet, we wait for the ident packet */
  return TRUE;
}

110 111

static GstCaps *
112
gst_rtp_speex_pay_getcaps (GstRTPBasePayload * payload, GstPad * pad,
113
    GstCaps * filter)
114 115 116 117 118
{
  GstCaps *otherpadcaps;
  GstCaps *caps;

  otherpadcaps = gst_pad_get_allowed_caps (payload->srcpad);
119
  caps = gst_pad_get_pad_template_caps (pad);
120 121 122

  if (otherpadcaps) {
    if (!gst_caps_is_empty (otherpadcaps)) {
123 124
      GstStructure *ps;
      GstStructure *s;
125 126
      gint clock_rate;

127 128 129 130
      ps = gst_caps_get_structure (otherpadcaps, 0);
      caps = gst_caps_make_writable (caps);
      s = gst_caps_get_structure (caps, 0);

131 132 133 134 135 136 137
      if (gst_structure_get_int (ps, "clock-rate", &clock_rate)) {
        gst_structure_fixate_field_nearest_int (s, "rate", clock_rate);
      }
    }
    gst_caps_unref (otherpadcaps);
  }

138 139 140 141 142 143 144
  if (filter) {
    GstCaps *tcaps = caps;

    caps = gst_caps_intersect_full (filter, tcaps, GST_CAPS_INTERSECT_FIRST);
    gst_caps_unref (tcaps);
  }

145 146 147
  return caps;
}

148 149 150 151 152
static gboolean
gst_rtp_speex_pay_parse_ident (GstRtpSPEEXPay * rtpspeexpay,
    const guint8 * data, guint size)
{
  guint32 version, header_size, rate, mode, nb_channels;
153
  GstRTPBasePayload *payload;
154
  gchar *cstr;
155
  gboolean res;
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190

  /* we need the header string (8), the version string (20), the version
   * and the header length. */
  if (size < 36)
    goto too_small;

  if (!g_str_has_prefix ((const gchar *) data, "Speex   "))
    goto wrong_header;

  /* skip header and version string */
  data += 28;

  version = GST_READ_UINT32_LE (data);
  if (version != 1)
    goto wrong_version;

  data += 4;
  /* ensure sizes */
  header_size = GST_READ_UINT32_LE (data);
  if (header_size < 80)
    goto header_too_small;

  if (size < header_size)
    goto payload_too_small;

  data += 4;
  rate = GST_READ_UINT32_LE (data);
  data += 4;
  mode = GST_READ_UINT32_LE (data);
  data += 8;
  nb_channels = GST_READ_UINT32_LE (data);

  GST_DEBUG_OBJECT (rtpspeexpay, "rate %d, mode %d, nb_channels %d",
      rate, mode, nb_channels);

191
  payload = GST_RTP_BASE_PAYLOAD (rtpspeexpay);
192

193
  gst_rtp_base_payload_set_options (payload, "audio", FALSE, "SPEEX", rate);
194
  cstr = g_strdup_printf ("%d", nb_channels);
195
  res = gst_rtp_base_payload_set_outcaps (payload, "encoding-params",
196 197
      G_TYPE_STRING, cstr, NULL);
  g_free (cstr);
198

199
  return res;
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

  /* ERRORS */
too_small:
  {
    GST_DEBUG_OBJECT (rtpspeexpay,
        "ident packet too small, need at least 32 bytes");
    return FALSE;
  }
wrong_header:
  {
    GST_DEBUG_OBJECT (rtpspeexpay,
        "ident packet does not start with \"Speex   \"");
    return FALSE;
  }
wrong_version:
  {
    GST_DEBUG_OBJECT (rtpspeexpay, "can only handle version 1, have version %d",
        version);
    return FALSE;
  }
header_too_small:
  {
    GST_DEBUG_OBJECT (rtpspeexpay,
        "header size too small, need at least 80 bytes, " "got only %d",
        header_size);
    return FALSE;
  }
payload_too_small:
  {
    GST_DEBUG_OBJECT (rtpspeexpay,
        "payload too small, need at least %d bytes, got only %d", header_size,
        size);
    return FALSE;
  }
234 235 236
}

static GstFlowReturn
237
gst_rtp_speex_pay_handle_buffer (GstRTPBasePayload * basepayload,
238 239
    GstBuffer * buffer)
{
240
  GstRtpSPEEXPay *rtpspeexpay;
Wim Taymans's avatar
Wim Taymans committed
241
  GstMapInfo map;
242
  GstBuffer *outbuf;
243
  GstClockTime timestamp, duration;
244 245
  GstFlowReturn ret;

246
  rtpspeexpay = GST_RTP_SPEEX_PAY (basepayload);
247

Wim Taymans's avatar
Wim Taymans committed
248
  gst_buffer_map (buffer, &map, GST_MAP_READ);
249 250 251 252 253

  switch (rtpspeexpay->packet) {
    case 0:
      /* ident packet. We need to parse the headers to construct the RTP
       * properties. */
254 255
      if (!gst_rtp_speex_pay_parse_ident (rtpspeexpay, map.data, map.size)) {
        gst_buffer_unmap (buffer, &map);
256
        goto parse_error;
257
      }
258 259

      ret = GST_FLOW_OK;
260
      gst_buffer_unmap (buffer, &map);
261 262 263 264
      goto done;
    case 1:
      /* comment packet, we ignore it */
      ret = GST_FLOW_OK;
265
      gst_buffer_unmap (buffer, &map);
266 267 268 269 270
      goto done;
    default:
      /* other packets go in the payload */
      break;
  }
271
  gst_buffer_unmap (buffer, &map);
272

273 274 275 276 277
  if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_GAP)) {
    ret = GST_FLOW_OK;
    goto done;
  }

278
  timestamp = GST_BUFFER_PTS (buffer);
279
  duration = GST_BUFFER_DURATION (buffer);
280 281 282

  /* FIXME, only one SPEEX frame per RTP packet for now */

283
  outbuf = gst_rtp_buffer_new_allocate (0, 0, 0);
284
  /* FIXME, assert for now */
285 286
  g_assert (gst_buffer_get_size (buffer) <=
      GST_RTP_BASE_PAYLOAD_MTU (rtpspeexpay));
287

288
  /* copy timestamp and duration */
289
  GST_BUFFER_PTS (outbuf) = timestamp;
290 291
  GST_BUFFER_DURATION (outbuf) = duration;

292
  gst_rtp_copy_audio_meta (basepayload, outbuf, buffer);
293 294
  outbuf = gst_buffer_append (outbuf, buffer);
  buffer = NULL;
295

296
  ret = gst_rtp_base_payload_push (basepayload, outbuf);
297

298
done:
299 300
  if (buffer)
    gst_buffer_unref (buffer);
301

302 303 304 305 306 307 308 309 310
  rtpspeexpay->packet++;

  return ret;

  /* ERRORS */
parse_error:
  {
    GST_ELEMENT_ERROR (rtpspeexpay, STREAM, DECODE, (NULL),
        ("Error parsing first identification packet."));
311
    gst_buffer_unref (buffer);
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
    return GST_FLOW_ERROR;
  }
}

static GstStateChangeReturn
gst_rtp_speex_pay_change_state (GstElement * element, GstStateChange transition)
{
  GstRtpSPEEXPay *rtpspeexpay;
  GstStateChangeReturn ret;

  rtpspeexpay = GST_RTP_SPEEX_PAY (element);

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      rtpspeexpay->packet = 0;
      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;
  }
342 343 344 345
  return ret;
}

gboolean
346
gst_rtp_speex_pay_plugin_init (GstPlugin * plugin)
347
{
348
  return gst_element_register (plugin, "rtpspeexpay",
349
      GST_RANK_SECONDARY, GST_TYPE_RTP_SPEEX_PAY);
350
}