gstrtpsv3vdepay.c 9.33 KB
Newer Older
1
/* GStreamer
2
 * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * 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 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 <string.h>

#include <gst/rtp/gstrtpbuffer.h>
27
#include <gst/video/video.h>
28
#include "gstrtpsv3vdepay.h"
29
#include "gstrtputils.h"
30

31 32 33
GST_DEBUG_CATEGORY (rtpsv3vdepay_debug);
#define GST_CAT_DEFAULT rtpsv3vdepay_debug

34 35 36 37 38 39 40 41 42 43 44 45 46
static GstStaticPadTemplate gst_rtp_sv3v_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-svq, " "svqversion = (int) 3")
    );

static GstStaticPadTemplate gst_rtp_sv3v_depay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("application/x-rtp, "
        "media = (string) \"video\", "
47
        "clock-rate = (int) 90000, "
48
        "encoding-name = (string) { \"X-SV3V-ES\", \"X-SORENSON-VIDEO\" , \"X-SORENSONVIDEO\" , \"X-SorensonVideo\" }")
49 50
    );

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
51 52
#define gst_rtp_sv3v_depay_parent_class parent_class
G_DEFINE_TYPE (GstRtpSV3VDepay, gst_rtp_sv3v_depay,
Wim Taymans's avatar
Wim Taymans committed
53
    GST_TYPE_RTP_BASE_DEPAYLOAD);
54 55 56 57 58 59

static void gst_rtp_sv3v_depay_finalize (GObject * object);

static GstStateChangeReturn gst_rtp_sv3v_depay_change_state (GstElement *
    element, GstStateChange transition);

Wim Taymans's avatar
Wim Taymans committed
60
static GstBuffer *gst_rtp_sv3v_depay_process (GstRTPBaseDepayload * depayload,
61
    GstRTPBuffer * rtp);
Wim Taymans's avatar
Wim Taymans committed
62
gboolean gst_rtp_sv3v_depay_setcaps (GstRTPBaseDepayload * filter,
63 64 65 66 67 68 69
    GstCaps * caps);

static void
gst_rtp_sv3v_depay_class_init (GstRtpSV3VDepayClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
Wim Taymans's avatar
Wim Taymans committed
70
  GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
71 72 73

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Wim Taymans's avatar
Wim Taymans committed
74
  gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
75

76
  gstrtpbasedepayload_class->process_rtp_packet = gst_rtp_sv3v_depay_process;
Wim Taymans's avatar
Wim Taymans committed
77
  gstrtpbasedepayload_class->set_caps = gst_rtp_sv3v_depay_setcaps;
78 79 80 81

  gobject_class->finalize = gst_rtp_sv3v_depay_finalize;

  gstelement_class->change_state = gst_rtp_sv3v_depay_change_state;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
82

83 84 85 86
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_rtp_sv3v_depay_src_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_rtp_sv3v_depay_sink_template);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
87

88
  gst_element_class_set_static_metadata (gstelement_class,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
89 90 91
      "RTP SVQ3 depayloader", "Codec/Depayloader/Network/RTP",
      "Extracts SVQ3 video from RTP packets (no RFC)",
      "Wim Taymans <wim.taymans@gmail.com>");
92 93 94
}

static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
95
gst_rtp_sv3v_depay_init (GstRtpSV3VDepay * rtpsv3vdepay)
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
{
  rtpsv3vdepay->adapter = gst_adapter_new ();
}

static void
gst_rtp_sv3v_depay_finalize (GObject * object)
{
  GstRtpSV3VDepay *rtpsv3vdepay;

  rtpsv3vdepay = GST_RTP_SV3V_DEPAY (object);

  g_object_unref (rtpsv3vdepay->adapter);
  rtpsv3vdepay->adapter = NULL;

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
113
/* only on the sink */
114
gboolean
Wim Taymans's avatar
Wim Taymans committed
115
gst_rtp_sv3v_depay_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps)
116 117
{
  GstStructure *structure = gst_caps_get_structure (caps, 0);
118
  gint clock_rate;
119

120
  if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
121
    clock_rate = 90000;         /* default */
122 123
  filter->clock_rate = clock_rate;

124 125
  /* will set caps later */

126 127 128 129
  return TRUE;
}

static GstBuffer *
130
gst_rtp_sv3v_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
131 132
{
  GstRtpSV3VDepay *rtpsv3vdepay;
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
  static struct
  {
    guint width, height;
  } resolutions[7] = {
    {
    160, 128}, {
    128, 96}, {
    176, 144}, {
    352, 288}, {
    704, 576}, {
    240, 180}, {
    320, 240}
  };
  gint payload_len;
  guint8 *payload;
  gboolean M;
  gboolean C, S, E;
  GstBuffer *outbuf = NULL;
151 152 153 154
  guint16 seq;

  rtpsv3vdepay = GST_RTP_SV3V_DEPAY (depayload);

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
155

156
  /* flush on sequence number gaps */
157
  seq = gst_rtp_buffer_get_seq (rtp);
158 159

  GST_DEBUG ("timestamp %" GST_TIME_FORMAT ", sequence number:%d",
160
      GST_TIME_ARGS (GST_BUFFER_PTS (rtp->buffer)), seq);
161

162
  if (seq != rtpsv3vdepay->nextseq) {
163
    GST_DEBUG ("Sequence discontinuity, clearing adapter");
164 165 166 167
    gst_adapter_clear (rtpsv3vdepay->adapter);
  }
  rtpsv3vdepay->nextseq = seq + 1;

168
  payload_len = gst_rtp_buffer_get_payload_len (rtp);
169 170 171
  if (payload_len < 3)
    goto bad_packet;

172
  payload = gst_rtp_buffer_get_payload (rtp);
173

174
  M = gst_rtp_buffer_get_marker (rtp);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

  /* This is all a guess:
   *                      1 1 1 1 1 1
   *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |0|C|S|E|0|0|0|0|0|0|0|0|0|0|0|0|
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *
   * C: config, packet contains config info
   * S: start, packet contains start of frame
   * E: end, packet contains end of frame
   */
  /* this seems to indicate a packet with a config string sent before each
   * keyframe */
  C = (payload[0] & 0x40) == 0x40;

  /* redundant with the RTP marker bit */
  S = (payload[0] & 0x20) == 0x20;
  E = (payload[0] & 0x10) == 0x10;

  GST_DEBUG ("M:%d, C:%d, S:%d, E:%d", M, C, S, E);

  GST_MEMDUMP ("incoming buffer", payload, payload_len);

  if (G_UNLIKELY (C)) {
    GstCaps *caps;
    GstBuffer *codec_data;
Wim Taymans's avatar
Wim Taymans committed
202
    GstMapInfo cmap;
203 204 205 206 207 208
    guint8 res;

    GST_DEBUG ("Configuration packet");

    /* if we already have caps, we don't need to do anything. FIXME, check if
     * something changed. */
Wim Taymans's avatar
Wim Taymans committed
209
    if (G_UNLIKELY (gst_pad_has_current_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
210
                (depayload)))) {
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
      GST_DEBUG ("Already configured, skipping config parsing");
      goto beach;
    }

    res = payload[2] >> 5;

    /* width and height, according to http://wiki.multimedia.cx/index.php?title=Sorenson_Video_1#Stream_Format_And_Header */
    if (G_LIKELY (res < 7)) {
      rtpsv3vdepay->width = resolutions[res].width;
      rtpsv3vdepay->height = resolutions[res].height;
    } else {
      /* extended width/height, they're contained in the following 24bit */
      rtpsv3vdepay->width = ((payload[2] & 0x1f) << 7) | (payload[3] >> 1);
      rtpsv3vdepay->height =
          (payload[3] & 0x1) << 11 | payload[4] << 3 | (payload[5] >> 5);
226 227
    }

228 229 230
    /* CodecData needs to be 'SEQH' + len (32bit) + data according to
     * ffmpeg's libavcodec/svq3.c:svq3_decode_init */
    codec_data = gst_buffer_new_and_alloc (payload_len + 6);
Wim Taymans's avatar
Wim Taymans committed
231 232 233 234 235 236
    gst_buffer_map (codec_data, &cmap, GST_MAP_WRITE);
    memcpy (cmap.data, "SEQH", 4);
    GST_WRITE_UINT32_LE (cmap.data + 4, payload_len - 2);
    memcpy (cmap.data + 8, payload + 2, payload_len - 2);
    GST_MEMDUMP ("codec_data", cmap.data, gst_buffer_get_size (codec_data));
    gst_buffer_unmap (codec_data, &cmap);
237

238 239 240 241 242
    caps = gst_caps_new_simple ("video/x-svq",
        "svqversion", G_TYPE_INT, 3,
        "width", G_TYPE_INT, rtpsv3vdepay->width,
        "height", G_TYPE_INT, rtpsv3vdepay->height,
        "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
Wim Taymans's avatar
Wim Taymans committed
243
    gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), caps);
244
    gst_caps_unref (caps);
245

246 247 248 249 250 251 252 253 254 255 256 257
    GST_DEBUG ("Depayloader now configured");

    rtpsv3vdepay->configured = TRUE;

    goto beach;
  }

  if (G_LIKELY (rtpsv3vdepay->configured)) {
    GstBuffer *tmpbuf;

    GST_DEBUG ("Storing incoming payload");
    /* store data in adapter, stip off 2 bytes header */
258
    tmpbuf = gst_rtp_buffer_get_payload_subbuffer (rtp, 2, -1);
259 260 261 262 263 264 265 266 267
    gst_adapter_push (rtpsv3vdepay->adapter, tmpbuf);

    if (G_UNLIKELY (M)) {
      /* frame is completed: push contents of adapter */
      guint avail;

      avail = gst_adapter_available (rtpsv3vdepay->adapter);
      GST_DEBUG ("Returning completed output buffer [%d bytes]", avail);
      outbuf = gst_adapter_take_buffer (rtpsv3vdepay->adapter, avail);
268
      gst_rtp_drop_non_video_meta (rtpsv3vdepay, outbuf);
269 270
    }
  }
271 272 273

beach:
  return outbuf;
274 275 276 277 278

  /* ERRORS */
bad_packet:
  {
    GST_ELEMENT_WARNING (rtpsv3vdepay, STREAM, DECODE,
279
        (NULL), ("Packet was too short"));
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    return NULL;
  }
}

static GstStateChangeReturn
gst_rtp_sv3v_depay_change_state (GstElement * element,
    GstStateChange transition)
{
  GstRtpSV3VDepay *rtpsv3vdepay;
  GstStateChangeReturn ret;

  rtpsv3vdepay = GST_RTP_SV3V_DEPAY (element);

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      gst_adapter_clear (rtpsv3vdepay->adapter);
      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_sv3v_depay_plugin_init (GstPlugin * plugin)
{
317 318 319
  GST_DEBUG_CATEGORY_INIT (rtpsv3vdepay_debug, "rtpsv3vdepay", 0,
      "RTP SV3V depayloader");

320
  return gst_element_register (plugin, "rtpsv3vdepay",
321
      GST_RANK_SECONDARY, GST_TYPE_RTP_SV3V_DEPAY);
322
}