gstrtpmp4vpay.c 18.7 KB
Newer Older
1
/* GStreamer
2
 * Copyright (C) <2005> Wim Taymans <wim.taymans@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
16
17
 * 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
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
18
19
20
21
22
23
24
25
26
27
 */

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

#include <string.h>

#include <gst/rtp/gstrtpbuffer.h>

28
#include "gstrtpmp4vpay.h"
29

30
GST_DEBUG_CATEGORY_STATIC (rtpmp4vpay_debug);
31
32
#define GST_CAT_DEFAULT (rtpmp4vpay_debug)

33
static GstStaticPadTemplate gst_rtp_mp4v_pay_sink_template =
34
    GST_STATIC_PAD_TEMPLATE ("sink",
35
36
37
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/mpeg,"
38
39
        "mpegversion=(int) 4," "systemstream=(boolean)false;"
        "video/x-xvid; video/x-divx")
40
41
    );

42
static GstStaticPadTemplate gst_rtp_mp4v_pay_src_template =
43
44
45
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
46
47
    GST_STATIC_CAPS ("application/x-rtp, "
        "media = (string) \"video\", "
48
        "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
49
50
        "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP4V-ES\""
        /* two string params
51
         *
52
53
         "profile-level-id = (string) [1,MAX]"
         "config = (string) [1,MAX]"
54
         */
55
56
57
    )
    );

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58
#define DEFAULT_SEND_CONFIG     FALSE
59
#define DEFAULT_BUFFER_LIST     FALSE
60
#define DEFAULT_CONFIG_INTERVAL 0
Wim Taymans's avatar
Wim Taymans committed
61
62
63
64

enum
{
  ARG_0,
65
  ARG_SEND_CONFIG,
66
67
  ARG_BUFFER_LIST,
  ARG_CONFIG_INTERVAL
Wim Taymans's avatar
Wim Taymans committed
68
69
};

70

71
static void gst_rtp_mp4v_pay_finalize (GObject * object);
72

73
static void gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
74
    const GValue * value, GParamSpec * pspec);
75
static void gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
76
77
    GValue * value, GParamSpec * pspec);

Wim Taymans's avatar
Wim Taymans committed
78
static gboolean gst_rtp_mp4v_pay_setcaps (GstRTPBasePayload * payload,
79
    GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
80
static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload *
81
    payload, GstBuffer * buffer);
Wim Taymans's avatar
Wim Taymans committed
82
static gboolean gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
83
    GstEvent * event);
84

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
85
#define gst_rtp_mp4v_pay_parent_class parent_class
Wim Taymans's avatar
Wim Taymans committed
86
G_DEFINE_TYPE (GstRtpMP4VPay, gst_rtp_mp4v_pay, GST_TYPE_RTP_BASE_PAYLOAD)
87

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
88
     static void gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass)
89
90
{
  GObjectClass *gobject_class;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
91
  GstElementClass *gstelement_class;
Wim Taymans's avatar
Wim Taymans committed
92
  GstRTPBasePayloadClass *gstrtpbasepayload_class;
93
94

  gobject_class = (GObjectClass *) klass;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
95
  gstelement_class = (GstElementClass *) klass;
Wim Taymans's avatar
Wim Taymans committed
96
  gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
97

98
99
  gobject_class->set_property = gst_rtp_mp4v_pay_set_property;
  gobject_class->get_property = gst_rtp_mp4v_pay_get_property;
Wim Taymans's avatar
Wim Taymans committed
100

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
101
102
103
104
105
106
107
108
109
110
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&gst_rtp_mp4v_pay_src_template));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&gst_rtp_mp4v_pay_sink_template));

  gst_element_class_set_details_simple (gstelement_class,
      "RTP MPEG4 Video payloader", "Codec/Payloader/Network/RTP",
      "Payload MPEG-4 video as RTP packets (RFC 3016)",
      "Wim Taymans <wim.taymans@gmail.com>");

Wim Taymans's avatar
Wim Taymans committed
111
112
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SEND_CONFIG,
      g_param_spec_boolean ("send-config", "Send Config",
113
114
          "Send the config parameters in RTP packets as well(deprecated "
          "see config-interval)",
115
          DEFAULT_SEND_CONFIG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
116

117
118
119
120
121
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFFER_LIST,
      g_param_spec_boolean ("buffer-list", "Buffer Array",
          "Use Buffer Arrays",
          DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

122
123
124
125
126
127
128
129
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CONFIG_INTERVAL,
      g_param_spec_uint ("config-interval", "Config Send Interval",
          "Send Config Insertion Interval in seconds (configuration headers "
          "will be multiplexed in the data stream when detected.) (0 = disabled)",
          0, 3600, DEFAULT_CONFIG_INTERVAL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
      );

130
  gobject_class->finalize = gst_rtp_mp4v_pay_finalize;
131

Wim Taymans's avatar
Wim Taymans committed
132
133
  gstrtpbasepayload_class->set_caps = gst_rtp_mp4v_pay_setcaps;
  gstrtpbasepayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer;
Wim Taymans's avatar
Wim Taymans committed
134
  gstrtpbasepayload_class->sink_event = gst_rtp_mp4v_pay_sink_event;
135
136
137

  GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0,
      "MP4 video RTP Payloader");
138
139
140
}

static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
141
gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay)
142
{
143
144
145
  rtpmp4vpay->adapter = gst_adapter_new ();
  rtpmp4vpay->rate = 90000;
  rtpmp4vpay->profile = 1;
146
  rtpmp4vpay->buffer_list = DEFAULT_BUFFER_LIST;
147
  rtpmp4vpay->send_config = DEFAULT_SEND_CONFIG;
148
  rtpmp4vpay->need_config = TRUE;
149
150
  rtpmp4vpay->config_interval = DEFAULT_CONFIG_INTERVAL;
  rtpmp4vpay->last_config = -1;
151

152
  rtpmp4vpay->config = NULL;
153
154
}

155
static void
156
gst_rtp_mp4v_pay_finalize (GObject * object)
157
{
158
  GstRtpMP4VPay *rtpmp4vpay;
159

160
  rtpmp4vpay = GST_RTP_MP4V_PAY (object);
161

162
  if (rtpmp4vpay->config) {
163
    gst_buffer_unref (rtpmp4vpay->config);
164
165
    rtpmp4vpay->config = NULL;
  }
166
167
  g_object_unref (rtpmp4vpay->adapter);
  rtpmp4vpay->adapter = NULL;
168

169
170
171
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

172
static gboolean
173
gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay)
174
{
175
176
  gchar *profile, *config;
  GValue v = { 0 };
177
  gboolean res;
178

179
  profile = g_strdup_printf ("%d", rtpmp4vpay->profile);
180
  g_value_init (&v, GST_TYPE_BUFFER);
181
  gst_value_set_buffer (&v, rtpmp4vpay->config);
182
183
  config = gst_value_serialize (&v);

Wim Taymans's avatar
Wim Taymans committed
184
  res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4vpay),
185
186
187
188
189
190
191
      "profile-level-id", G_TYPE_STRING, profile,
      "config", G_TYPE_STRING, config, NULL);

  g_value_unset (&v);

  g_free (profile);
  g_free (config);
192
193

  return res;
194
195
196
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
197
gst_rtp_mp4v_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
198
{
199
  GstRtpMP4VPay *rtpmp4vpay;
200
  GstStructure *structure;
201
  const GValue *codec_data;
202
  gboolean res;
203

204
  rtpmp4vpay = GST_RTP_MP4V_PAY (payload);
205

Wim Taymans's avatar
Wim Taymans committed
206
  gst_rtp_base_payload_set_options (payload, "video", TRUE, "MP4V-ES",
207
208
      rtpmp4vpay->rate);

209
210
  res = TRUE;

211
  structure = gst_caps_get_structure (caps, 0);
212
213
214
215
  codec_data = gst_structure_get_value (structure, "codec_data");
  if (codec_data) {
    GST_LOG_OBJECT (rtpmp4vpay, "got codec_data");
    if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
216
217
      GstBuffer *buffer;

218
      buffer = gst_value_get_buffer (codec_data);
219

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
220
      if (gst_buffer_get_size (buffer) < 5)
221
222
        goto done;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
223
      gst_buffer_extract (buffer, 4, &rtpmp4vpay->profile, 1);
224
      GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
225
          rtpmp4vpay->profile);
226
227
228
229

      if (rtpmp4vpay->config)
        gst_buffer_unref (rtpmp4vpay->config);
      rtpmp4vpay->config = gst_buffer_copy (buffer);
230
      res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay);
231
232
233
234
    }
  }

done:
235
  return res;
236
237
}

238
239
240
241
242
243
static void
gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay)
{
  gst_adapter_clear (rtpmp4vpay->adapter);
}

244
static GstFlowReturn
245
gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay)
246
247
248
{
  guint avail;
  GstBuffer *outbuf;
249
  GstBuffer *outbuf_data = NULL;
250
  GstFlowReturn ret;
251
  GstBufferList *list = NULL;
252
253
254
255
256
257

  /* the data available in the adapter is either smaller
   * than the MTU or bigger. In the case it is smaller, the complete
   * adapter contents can be put in one packet. In the case the
   * adapter has more than one MTU, we need to split the MP4V data
   * over multiple packets. */
258
  avail = gst_adapter_available (rtpmp4vpay->adapter);
259

260
  if (rtpmp4vpay->config == NULL && rtpmp4vpay->need_config) {
261
262
263
264
265
    /* when we don't have a config yet, flush things out */
    gst_adapter_flush (rtpmp4vpay->adapter, avail);
    avail = 0;
  }

266
267
268
  if (!avail)
    return GST_FLOW_OK;

269
270
  ret = GST_FLOW_OK;

271
272
273
274
275
276
277
  if (rtpmp4vpay->buffer_list) {
    /* Use buffer lists. Each frame will be put into a list
     * of buffers and the whole list will be pushed downstream
     * at once */
    list = gst_buffer_list_new ();
  }

278
279
280
281
  while (avail > 0) {
    guint towrite;
    guint payload_len;
    guint packet_len;
282
    GstRTPBuffer rtp = { NULL };
283
284

    /* this will be the total lenght of the packet */
285
    packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0);
286
287

    /* fill one MTU or all available bytes */
Wim Taymans's avatar
Wim Taymans committed
288
    towrite = MIN (packet_len, GST_RTP_BASE_PAYLOAD_MTU (rtpmp4vpay));
289
290

    /* this is the payload length */
291
    payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
292

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
293
294
295
    /* create buffer without payload. The payload will be put
     * in next buffer instead. Both buffers will be merged */
    outbuf = gst_rtp_buffer_new_allocate (0, 0, 0);
296

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
297
298
    /* Take buffer with the payload from the adapter */
    outbuf_data = gst_adapter_take_buffer (rtpmp4vpay->adapter, payload_len);
299
300
301

    avail -= payload_len;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
302
303
304
305
306
    gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
    gst_rtp_buffer_set_marker (&rtp, avail == 0);
    gst_rtp_buffer_unmap (&rtp);

    outbuf = gst_buffer_join (outbuf, outbuf_data);
307

308
    GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4vpay->first_timestamp;
309

310
    if (rtpmp4vpay->buffer_list) {
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
311
312
      /* add to list */
      gst_buffer_list_insert (list, -1, outbuf);
313
    } else {
Wim Taymans's avatar
Wim Taymans committed
314
      ret =
Wim Taymans's avatar
Wim Taymans committed
315
          gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmp4vpay), outbuf);
316
317
318
319
320
321
    }
  }

  if (rtpmp4vpay->buffer_list) {
    /* push the whole buffer list at once */
    ret =
Wim Taymans's avatar
Wim Taymans committed
322
        gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpmp4vpay),
Wim Taymans's avatar
Wim Taymans committed
323
        list);
324
325
326
327
328
  }

  return ret;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
329
330
331
332
333
334
#define VOS_STARTCODE                   0x000001B0
#define VOS_ENDCODE                     0x000001B1
#define USER_DATA_STARTCODE             0x000001B2
#define GOP_STARTCODE                   0x000001B3
#define VISUAL_OBJECT_STARTCODE         0x000001B5
#define VOP_STARTCODE                   0x000001B6
335
336

static gboolean
337
gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
338
    gint * strip, gboolean * vopi)
339
340
341
{
  guint32 code;
  gboolean result;
342
  *vopi = FALSE;
343

Wim Taymans's avatar
Wim Taymans committed
344
345
  *strip = 0;

346
347
348
349
  if (size < 5)
    return FALSE;

  code = GST_READ_UINT32_BE (data);
350
  GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
351
352
353

  switch (code) {
    case VOS_STARTCODE:
354
    case 0x00000101:
355
356
357
358
359
360
    {
      gint i;
      guint8 profile;
      gboolean newprofile = FALSE;
      gboolean equal;

361
362
363
      if (code == VOS_STARTCODE) {
        /* profile_and_level_indication */
        profile = data[4];
364

365
        GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile);
366

367
368
369
370
        if (profile != enc->profile) {
          newprofile = TRUE;
          enc->profile = profile;
        }
371
372
373
374
375
376
377
378
379
380
381
382
383
384
      }

      /* up to the next GOP_STARTCODE or VOP_STARTCODE is
       * the config information */
      code = 0xffffffff;
      for (i = 5; i < size - 4; i++) {
        code = (code << 8) | data[i];
        if (code == GOP_STARTCODE || code == VOP_STARTCODE)
          break;
      }
      i -= 3;
      /* see if config changed */
      equal = FALSE;
      if (enc->config) {
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
385
386
        if (gst_buffer_get_size (enc->config) == i) {
          equal = gst_buffer_memcmp (enc->config, 0, data, i) == 0;
387
388
389
390
391
392
393
        }
      }
      /* if config string changed or new profile, make new caps */
      if (!equal || newprofile) {
        if (enc->config)
          gst_buffer_unref (enc->config);
        enc->config = gst_buffer_new_and_alloc (i);
Wim Taymans's avatar
Wim Taymans committed
394
395
396

        gst_buffer_fill (enc->config, 0, data, i);

397
        gst_rtp_mp4v_pay_new_caps (enc);
398
      }
Wim Taymans's avatar
Wim Taymans committed
399
400
      *strip = i;
      /* we need to flush out the current packet. */
401
402
403
404
      result = TRUE;
      break;
    }
    case VOP_STARTCODE:
405
      GST_DEBUG_OBJECT (enc, "VOP");
Wim Taymans's avatar
Wim Taymans committed
406
      /* VOP startcode, we don't have to flush the packet */
407
      result = FALSE;
408
409
410
411
412
413
414
415
416
417
      /* vop-coding-type == I-frame */
      if (size > 4 && (data[4] >> 6 == 0)) {
        GST_DEBUG_OBJECT (enc, "VOP-I");
        *vopi = TRUE;
      }
      break;
    case GOP_STARTCODE:
      GST_DEBUG_OBJECT (enc, "GOP");
      *vopi = TRUE;
      result = TRUE;
418
      break;
419
420
421
422
    case 0x00000100:
      enc->need_config = FALSE;
      result = TRUE;
      break;
423
    default:
424
425
426
427
428
429
430
431
      if (code >= 0x20 && code <= 0x2f) {
        GST_DEBUG_OBJECT (enc, "short header");
        result = FALSE;
      } else {
        GST_DEBUG_OBJECT (enc, "other startcode");
        /* all other startcodes need a flush */
        result = TRUE;
      }
432
433
434
435
436
      break;
  }
  return result;
}

437
438
439
/* we expect buffers starting on startcodes. 
 */
static GstFlowReturn
Wim Taymans's avatar
Wim Taymans committed
440
gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload * basepayload,
441
    GstBuffer * buffer)
442
{
443
  GstRtpMP4VPay *rtpmp4vpay;
444
  GstFlowReturn ret;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
445
  guint avail;
446
  guint packet_len;
Wim Taymans's avatar
Wim Taymans committed
447
448
  GstMapInfo map;
  gsize size;
449
  gboolean flush;
Wim Taymans's avatar
Wim Taymans committed
450
  gint strip;
451
  GstClockTime timestamp, duration;
452
453
  gboolean vopi;
  gboolean send_config;
Wim Taymans's avatar
Wim Taymans committed
454
455

  ret = GST_FLOW_OK;
456
  send_config = FALSE;
457

458
  rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
459

Wim Taymans's avatar
Wim Taymans committed
460
461
  gst_buffer_map (buffer, &map, GST_MAP_READ);
  size = map.size;
462
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
463
  duration = GST_BUFFER_DURATION (buffer);
464
  avail = gst_adapter_available (rtpmp4vpay->adapter);
465

466
467
468
  if (duration == -1)
    duration = 0;

Wim Taymans's avatar
Wim Taymans committed
469
470
  /* empty buffer, take timestamp */
  if (avail == 0) {
471
    rtpmp4vpay->first_timestamp = timestamp;
472
    rtpmp4vpay->duration = 0;
Wim Taymans's avatar
Wim Taymans committed
473
474
  }

475
  /* depay incomming data and see if we need to start a new RTP
476
   * packet */
Wim Taymans's avatar
Wim Taymans committed
477
478
479
  flush =
      gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, map.data, size, &strip, &vopi);
  gst_buffer_unmap (buffer, &map);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
480

Wim Taymans's avatar
Wim Taymans committed
481
482
  if (strip) {
    /* strip off config if requested */
483
    if (!(rtpmp4vpay->config_interval > 0)) {
Wim Taymans's avatar
Wim Taymans committed
484
485
      GstBuffer *subbuf;

486
      GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
487
          (gint) size - strip);
488

Wim Taymans's avatar
Wim Taymans committed
489
      /* strip off header */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
490
491
      subbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY, strip,
          size - strip);
492
      GST_BUFFER_TIMESTAMP (subbuf) = timestamp;
Wim Taymans's avatar
Wim Taymans committed
493
494
495
      gst_buffer_unref (buffer);
      buffer = subbuf;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
496
      size = gst_buffer_get_size (buffer);
497
498
499
500
501
502
503
    } else {
      GST_LOG_OBJECT (rtpmp4vpay, "found config in stream");
      rtpmp4vpay->last_config = timestamp;
    }
  }

  /* there is a config request, see if we need to insert it */
504
  if (vopi && (rtpmp4vpay->config_interval > 0) && rtpmp4vpay->config) {
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
    if (rtpmp4vpay->last_config != -1) {
      guint64 diff;

      GST_LOG_OBJECT (rtpmp4vpay,
          "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
          GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtpmp4vpay->last_config));

      /* calculate diff between last config in milliseconds */
      if (timestamp > rtpmp4vpay->last_config) {
        diff = timestamp - rtpmp4vpay->last_config;
      } else {
        diff = 0;
      }

      GST_DEBUG_OBJECT (rtpmp4vpay,
          "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));

      /* bigger than interval, queue config */
      /* FIXME should convert timestamps to running time */
      if (GST_TIME_AS_SECONDS (diff) >= rtpmp4vpay->config_interval) {
        GST_DEBUG_OBJECT (rtpmp4vpay, "time to send config");
        send_config = TRUE;
      }
    } else {
      /* no known previous config time, send now */
      GST_DEBUG_OBJECT (rtpmp4vpay, "no previous config time, send now");
      send_config = TRUE;
    }

    if (send_config) {
      /* we need to send config now first */
      GstBuffer *superbuf;

      GST_LOG_OBJECT (rtpmp4vpay, "inserting config in stream");

      /* insert header */
      superbuf = gst_buffer_merge (rtpmp4vpay->config, buffer);

      GST_BUFFER_TIMESTAMP (superbuf) = timestamp;
      gst_buffer_unref (buffer);
      buffer = superbuf;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
547
      size = gst_buffer_get_size (buffer);
548
549
550
551

      if (timestamp != -1) {
        rtpmp4vpay->last_config = timestamp;
      }
Wim Taymans's avatar
Wim Taymans committed
552
553
    }
  }
554

Wim Taymans's avatar
Wim Taymans committed
555
556
  /* if we need to flush, do so now */
  if (flush) {
557
    ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
558
    rtpmp4vpay->first_timestamp = timestamp;
559
    rtpmp4vpay->duration = 0;
560
    avail = 0;
561
562
  }

Wim Taymans's avatar
Wim Taymans committed
563
  /* get packet length of data and see if we exceeded MTU. */
564
  packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0);
565

Wim Taymans's avatar
Wim Taymans committed
566
  if (gst_rtp_base_payload_is_filled (basepayload,
567
568
          packet_len, rtpmp4vpay->duration + duration)) {
    ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
569
    rtpmp4vpay->first_timestamp = timestamp;
570
    rtpmp4vpay->duration = 0;
571
572
  }

573
  /* push new data */
574
  gst_adapter_push (rtpmp4vpay->adapter, buffer);
575

576
  rtpmp4vpay->duration += duration;
577

578
579
580
  return ret;
}

581
static gboolean
Wim Taymans's avatar
Wim Taymans committed
582
gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay, GstEvent * event)
583
584
585
{
  GstRtpMP4VPay *rtpmp4vpay;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
586
  rtpmp4vpay = GST_RTP_MP4V_PAY (pay);
587
588
589
590

  GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event));

  switch (GST_EVENT_TYPE (event)) {
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
591
    case GST_EVENT_SEGMENT:
592
593
594
    case GST_EVENT_EOS:
      /* This flush call makes sure that the last buffer is always pushed
       * to the base payloader */
595
596
597
598
599
600
601
602
603
      gst_rtp_mp4v_pay_flush (rtpmp4vpay);
      break;
    case GST_EVENT_FLUSH_STOP:
      gst_rtp_mp4v_pay_empty (rtpmp4vpay);
      break;
    default:
      break;
  }

604
  /* let parent handle event too */
Wim Taymans's avatar
Wim Taymans committed
605
  return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (pay, event);
606
607
}

Wim Taymans's avatar
Wim Taymans committed
608
static void
609
gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
610
611
    const GValue * value, GParamSpec * pspec)
{
612
  GstRtpMP4VPay *rtpmp4vpay;
Wim Taymans's avatar
Wim Taymans committed
613

614
  rtpmp4vpay = GST_RTP_MP4V_PAY (object);
Wim Taymans's avatar
Wim Taymans committed
615
616
617

  switch (prop_id) {
    case ARG_SEND_CONFIG:
618
      rtpmp4vpay->send_config = g_value_get_boolean (value);
619
620
621
622
      /* send the configuration once every minute */
      if (rtpmp4vpay->send_config && !(rtpmp4vpay->config_interval > 0)) {
        rtpmp4vpay->config_interval = 60;
      }
Wim Taymans's avatar
Wim Taymans committed
623
      break;
624
625
626
    case ARG_BUFFER_LIST:
      rtpmp4vpay->buffer_list = g_value_get_boolean (value);
      break;
627
628
629
    case ARG_CONFIG_INTERVAL:
      rtpmp4vpay->config_interval = g_value_get_uint (value);
      break;
Wim Taymans's avatar
Wim Taymans committed
630
631
632
633
634
635
    default:
      break;
  }
}

static void
636
gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
Wim Taymans's avatar
Wim Taymans committed
637
638
    GValue * value, GParamSpec * pspec)
{
639
  GstRtpMP4VPay *rtpmp4vpay;
Wim Taymans's avatar
Wim Taymans committed
640

641
  rtpmp4vpay = GST_RTP_MP4V_PAY (object);
Wim Taymans's avatar
Wim Taymans committed
642
643
644

  switch (prop_id) {
    case ARG_SEND_CONFIG:
645
      g_value_set_boolean (value, rtpmp4vpay->send_config);
Wim Taymans's avatar
Wim Taymans committed
646
      break;
647
648
649
    case ARG_BUFFER_LIST:
      g_value_set_boolean (value, rtpmp4vpay->buffer_list);
      break;
650
651
652
    case ARG_CONFIG_INTERVAL:
      g_value_set_uint (value, rtpmp4vpay->config_interval);
      break;
Wim Taymans's avatar
Wim Taymans committed
653
654
655
656
657
    default:
      break;
  }
}

658
gboolean
659
gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin)
660
{
661
  return gst_element_register (plugin, "rtpmp4vpay",
662
      GST_RANK_SECONDARY, GST_TYPE_RTP_MP4V_PAY);
663
}