1
/* ASF RTP Payloader plugin for GStreamer
2
* Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Library General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the
16
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
21
* - this element doesn't follow (max/min) time properties,
22
* is it possible to do it with a container format?
29
#include <gst/rtp/gstrtpbuffer.h>
32
#include "gstrtpasfpay.h"
34
GST_DEBUG_CATEGORY_STATIC (rtpasfpay_debug);
35
#define GST_CAT_DEFAULT (rtpasfpay_debug)
37
/* elementfactory information */
38
static const GstElementDetails gst_rtp_asf_pay_details =
39
GST_ELEMENT_DETAILS ("RTP ASF payloader",
40
"Codec/Payloader/Network",
41
"Payload-encodes ASF into RTP packets (MS_RTSP)",
42
"Thiago Santos <thiagoss@embedded.ufcg.edu.br>");
44
static GstStaticPadTemplate gst_rtp_asf_pay_sink_template =
45
GST_STATIC_PAD_TEMPLATE ("sink",
48
GST_STATIC_CAPS ("video/x-ms-asf, " "parsed = (boolean) true")
51
static GstStaticPadTemplate gst_rtp_asf_pay_src_template =
52
GST_STATIC_PAD_TEMPLATE ("src",
55
GST_STATIC_CAPS ("application/x-rtp, "
56
"media = (string) {\"audio\", \"video\", \"application\"}, "
57
"clock-rate = (int) 1000, " "encoding-name = (string) \"X-ASF-PF\"")
61
gst_rtp_asf_pay_handle_buffer (GstBaseRTPPayload * rtppay, GstBuffer * buffer);
63
gst_rtp_asf_pay_set_caps (GstBaseRTPPayload * rtppay, GstCaps * caps);
65
GST_BOILERPLATE (GstRtpAsfPay, gst_rtp_asf_pay, GstBaseRTPPayload,
66
GST_TYPE_BASE_RTP_PAYLOAD);
69
gst_rtp_asf_pay_init (GstRtpAsfPay * rtpasfpay, GstRtpAsfPayClass * klass)
71
rtpasfpay->first_ts = 0;
72
rtpasfpay->config = NULL;
73
rtpasfpay->packets_count = 0;
74
rtpasfpay->state = ASF_NOT_STARTED;
75
rtpasfpay->headers = NULL;
76
rtpasfpay->current = NULL;
80
gst_rtp_asf_pay_finalize (GObject * object)
82
GstRtpAsfPay *rtpasfpay;
83
rtpasfpay = GST_RTP_ASF_PAY (object);
84
g_free (rtpasfpay->config);
85
if (rtpasfpay->headers)
86
gst_buffer_unref (rtpasfpay->headers);
87
G_OBJECT_CLASS (parent_class)->finalize (object);
91
gst_rtp_asf_pay_base_init (gpointer klass)
93
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
95
gst_element_class_add_pad_template (element_class,
96
gst_static_pad_template_get (&gst_rtp_asf_pay_sink_template));
97
gst_element_class_add_pad_template (element_class,
98
gst_static_pad_template_get (&gst_rtp_asf_pay_src_template));
99
gst_element_class_set_details (element_class, &gst_rtp_asf_pay_details);
103
gst_rtp_asf_pay_class_init (GstRtpAsfPayClass * klass)
105
GObjectClass *gobject_class;
106
GstBaseRTPPayloadClass *gstbasertppayload_class;
108
gobject_class = (GObjectClass *) klass;
109
gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
111
gobject_class->finalize = gst_rtp_asf_pay_finalize;
113
gstbasertppayload_class->handle_buffer = gst_rtp_asf_pay_handle_buffer;
114
gstbasertppayload_class->set_caps = gst_rtp_asf_pay_set_caps;
116
GST_DEBUG_CATEGORY_INIT (rtpasfpay_debug, "rtpasfpay", 0,
117
"ASF RTP Payloader");
121
gst_rtp_asf_pay_set_caps (GstBaseRTPPayload * rtppay, GstCaps * caps)
123
/* FIXME change application for the actual content */
124
gst_basertppayload_set_options (rtppay, "application", TRUE, "X-ASF-PF",
130
gst_rtp_asf_pay_handle_packet (GstRtpAsfPay * rtpasfpay, GstBuffer * buffer)
132
GstBaseRTPPayload *rtppay;
133
GstAsfPacketInfo *packetinfo;
136
guint32 packet_util_size;
137
guint32 packet_offset;
139
GstFlowReturn ret = GST_FLOW_OK;
141
rtppay = GST_BASE_RTP_PAYLOAD (rtpasfpay);
142
packetinfo = &rtpasfpay->packetinfo;
144
if (!gst_asf_parse_packet (buffer, packetinfo, TRUE)) {
145
GST_ERROR_OBJECT (rtpasfpay, "Error while parsing asf packet");
146
gst_buffer_unref (buffer);
147
return GST_FLOW_ERROR;
150
if (packetinfo->packet_size == 0)
151
packetinfo->packet_size = rtpasfpay->asfinfo.packet_size;
153
GST_LOG_OBJECT (rtpasfpay, "Packet size: %" G_GUINT32_FORMAT
154
", padding: %" G_GUINT32_FORMAT, packetinfo->packet_size,
155
packetinfo->padding);
157
/* update padding field to 0 */
158
if (packetinfo->padding > 0) {
159
GstAsfPacketInfo info;
160
/* find padding field offset */
161
guint offset = packetinfo->err_cor_len + 2 +
162
gst_asf_get_var_size_field_len (packetinfo->packet_field_type) +
163
gst_asf_get_var_size_field_len (packetinfo->seq_field_type);
164
buffer = gst_buffer_make_writable (buffer);
165
switch (packetinfo->padd_field_type) {
166
case ASF_FIELD_TYPE_DWORD:
167
GST_WRITE_UINT32_LE (&(GST_BUFFER_DATA (buffer)[offset]), 0);
169
case ASF_FIELD_TYPE_WORD:
170
GST_WRITE_UINT16_LE (&(GST_BUFFER_DATA (buffer)[offset]), 0);
172
case ASF_FIELD_TYPE_BYTE:
173
GST_BUFFER_DATA (buffer)[offset] = 0;
175
case ASF_FIELD_TYPE_NONE:
179
gst_asf_parse_packet (buffer, &info, FALSE);
182
packet_util_size = packetinfo->packet_size - packetinfo->padding;
184
while (packet_util_size > 0) {
185
/* Even if we don't fill completely an output buffer we
186
* push it when we add an fragment. Because it seems that
187
* it is not possible to determine where a asf packet
188
* fragment ends inside a rtp packet payload.
189
* This flag tells us to push the packet.
191
gboolean force_push = FALSE;
193
/* we have no output buffer pending, create one */
194
if (rtpasfpay->current == NULL) {
195
GST_LOG_OBJECT (rtpasfpay, "Creating new output buffer");
197
gst_rtp_buffer_new_allocate_len (GST_BASE_RTP_PAYLOAD_MTU (rtpasfpay),
199
rtpasfpay->cur_off = gst_rtp_buffer_get_header_len (rtpasfpay->current);
200
rtpasfpay->has_ts = FALSE;
201
rtpasfpay->marker = FALSE;
203
data = GST_BUFFER_DATA (rtpasfpay->current) + rtpasfpay->cur_off;
204
size_left = GST_BUFFER_SIZE (rtpasfpay->current) - rtpasfpay->cur_off;
206
GST_DEBUG_OBJECT (rtpasfpay, "Input buffer bytes consumed: %"
207
G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT, packet_offset,
208
GST_BUFFER_SIZE (buffer));
210
GST_DEBUG_OBJECT (rtpasfpay, "Output rtpbuffer status");
211
GST_DEBUG_OBJECT (rtpasfpay, "Current offset: %" G_GUINT32_FORMAT,
213
GST_DEBUG_OBJECT (rtpasfpay, "Size left: %" G_GUINT32_FORMAT, size_left);
214
GST_DEBUG_OBJECT (rtpasfpay, "Has ts: %s",
215
rtpasfpay->has_ts ? "yes" : "no");
216
if (rtpasfpay->has_ts) {
217
GST_DEBUG_OBJECT (rtpasfpay, "Ts: %" G_GUINT32_FORMAT, rtpasfpay->ts);
221
if (packetinfo->has_keyframe) {
222
flags = flags | 0x80;
224
flags = flags | 0x20; /* Relative timestamp is present */
226
if (!rtpasfpay->has_ts) {
227
/* this is the first asf packet, its send time is the
228
* rtp packet timestamp */
229
rtpasfpay->has_ts = TRUE;
230
rtpasfpay->ts = packetinfo->send_time;
233
if (GST_BUFFER_SIZE (rtpasfpay->current) - rtpasfpay->cur_off >=
234
packet_util_size + 8) {
235
/* enough space for the rest of the packet */
236
if (packet_offset == 0) {
237
flags = flags | 0x40;
238
GST_WRITE_UINT24_BE (data + 1, packet_util_size);
240
GST_WRITE_UINT24_BE (data + 1, packet_offset);
244
GST_WRITE_UINT32_BE (data + 4,
245
(gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts);
246
memcpy (data + 8, GST_BUFFER_DATA (buffer) + packet_offset,
249
/* updating status variables */
250
rtpasfpay->cur_off += 8 + packet_util_size;
251
size_left -= packet_util_size + 8;
252
packet_offset += packet_util_size;
253
packet_util_size = 0;
254
rtpasfpay->marker = TRUE;
256
/* fragment packet */
258
GST_WRITE_UINT24_BE (data + 1, packet_offset);
259
GST_WRITE_UINT32_BE (data + 4,
260
(gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts);
261
memcpy (data + 8, GST_BUFFER_DATA (buffer) + packet_offset,
264
/* updating status variables */
265
rtpasfpay->cur_off += size_left;
266
packet_offset += size_left - 8;
267
packet_util_size -= size_left - 8;
272
/* there is not enough room for any more buffers */
273
if (force_push || size_left <= 8) {
275
if (size_left != 0) {
276
/* trim remaining bytes not used */
277
GstBuffer *aux = gst_buffer_create_sub (rtpasfpay->current, 0,
278
GST_BUFFER_SIZE (rtpasfpay->current) - size_left);
279
gst_buffer_unref (rtpasfpay->current);
280
rtpasfpay->current = aux;
282
gst_rtp_buffer_set_ssrc (rtpasfpay->current, rtppay->current_ssrc);
283
gst_rtp_buffer_set_marker (rtpasfpay->current, rtpasfpay->marker);
284
gst_rtp_buffer_set_payload_type (rtpasfpay->current,
285
GST_BASE_RTP_PAYLOAD_PT (rtppay));
286
gst_rtp_buffer_set_seq (rtpasfpay->current, rtppay->seqnum + 1);
287
gst_rtp_buffer_set_timestamp (rtpasfpay->current, packetinfo->send_time);
289
GST_BUFFER_TIMESTAMP (rtpasfpay->current) = GST_BUFFER_TIMESTAMP (buffer);
291
gst_buffer_set_caps (rtpasfpay->current,
292
GST_PAD_CAPS (GST_BASE_RTP_PAYLOAD_SRCPAD (rtppay)));
295
rtppay->timestamp = packetinfo->send_time;
297
GST_DEBUG_OBJECT (rtpasfpay, "Pushing rtp buffer");
299
gst_pad_push (GST_BASE_RTP_PAYLOAD_SRCPAD (rtppay),
301
rtpasfpay->current = NULL;
302
if (ret != GST_FLOW_OK) {
303
gst_buffer_unref (buffer);
308
gst_buffer_unref (buffer);
313
gst_rtp_asf_pay_parse_headers (GstRtpAsfPay * rtpasfpay)
315
GstFlowReturn ret = GST_FLOW_OK;
317
g_return_val_if_fail (rtpasfpay->headers, GST_FLOW_ERROR);
319
if (!gst_asf_parse_headers (rtpasfpay->headers, &rtpasfpay->asfinfo))
322
GST_DEBUG_OBJECT (rtpasfpay, "Packets number: %" G_GUINT64_FORMAT,
323
rtpasfpay->asfinfo.packets_count);
324
GST_DEBUG_OBJECT (rtpasfpay, "Packets size: %" G_GUINT32_FORMAT,
325
rtpasfpay->asfinfo.packet_size);
326
GST_DEBUG_OBJECT (rtpasfpay, "Broadcast mode: %s",
327
rtpasfpay->asfinfo.broadcast ? "true" : "false");
329
/* get the config for caps */
330
g_free (rtpasfpay->config);
331
rtpasfpay->config = g_base64_encode (GST_BUFFER_DATA (rtpasfpay->headers),
332
GST_BUFFER_SIZE (rtpasfpay->headers));
333
GST_DEBUG_OBJECT (rtpasfpay, "Serialized headers to base64 string %s",
336
g_assert (rtpasfpay->config != NULL);
337
GST_DEBUG_OBJECT (rtpasfpay, "Setting optional caps values: maxps=%"
338
G_GUINT32_FORMAT " and config=%s", rtpasfpay->asfinfo.packet_size,
341
g_strdup_printf ("%" G_GUINT32_FORMAT, rtpasfpay->asfinfo.packet_size);
342
gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpasfpay), "maxps",
343
G_TYPE_STRING, maxps, "config", G_TYPE_STRING, rtpasfpay->config, NULL);
349
ret = GST_FLOW_ERROR;
350
GST_ERROR_OBJECT (rtpasfpay, "Error while parsing headers");
351
return GST_FLOW_ERROR;
355
gst_rtp_asf_pay_handle_buffer (GstBaseRTPPayload * rtppay, GstBuffer * buffer)
357
GstRtpAsfPay *rtpasfpay = GST_RTP_ASF_PAY_CAST (rtppay);
359
if (G_UNLIKELY (rtpasfpay->state == ASF_END)) {
360
GST_LOG_OBJECT (rtpasfpay,
361
"Dropping buffer as we already pushed all packets");
362
gst_buffer_unref (buffer);
363
return GST_FLOW_UNEXPECTED; /* we already finished our job */
367
* we only accept if they are in a single buffer */
368
if (G_UNLIKELY (rtpasfpay->state == ASF_NOT_STARTED)) {
371
if (GST_BUFFER_SIZE (buffer) < 24) { /* guid+object size size */
372
GST_ERROR_OBJECT (rtpasfpay,
373
"Buffer too small, smaller than a Guid and object size");
374
gst_buffer_unref (buffer);
375
return GST_FLOW_ERROR;
378
header_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buffer),
379
&(guids[ASF_HEADER_OBJECT_INDEX]));
380
if (header_size > 0) {
381
GST_DEBUG_OBJECT (rtpasfpay, "ASF header guid received, size %"
382
G_GUINT64_FORMAT, header_size);
384
if (GST_BUFFER_SIZE (buffer) < header_size) {
385
GST_ERROR_OBJECT (rtpasfpay, "Headers should be contained in a single"
387
gst_buffer_unref (buffer);
388
return GST_FLOW_ERROR;
390
rtpasfpay->state = ASF_DATA_OBJECT;
392
/* clear previous headers, if any */
393
if (rtpasfpay->headers) {
394
gst_buffer_unref (rtpasfpay->headers);
397
GST_DEBUG_OBJECT (rtpasfpay, "Storing headers");
398
if (GST_BUFFER_SIZE (buffer) == header_size) {
399
rtpasfpay->headers = buffer;
402
/* headers are a subbuffer of thie buffer */
403
GstBuffer *aux = gst_buffer_create_sub (buffer, header_size,
404
GST_BUFFER_SIZE (buffer) - header_size);
405
rtpasfpay->headers = gst_buffer_create_sub (buffer, 0, header_size);
406
gst_buffer_replace (&buffer, aux);
410
GST_ERROR_OBJECT (rtpasfpay, "Missing ASF header start");
411
gst_buffer_unref (buffer);
412
return GST_FLOW_ERROR;
416
if (G_UNLIKELY (rtpasfpay->state == ASF_DATA_OBJECT)) {
417
if (GST_BUFFER_SIZE (buffer) != ASF_DATA_OBJECT_SIZE) {
418
GST_ERROR_OBJECT (rtpasfpay, "Received buffer of different size of "
419
"the data object header");
420
gst_buffer_unref (buffer);
421
return GST_FLOW_ERROR;
424
if (gst_asf_match_guid (GST_BUFFER_DATA (buffer),
425
&(guids[ASF_DATA_OBJECT_INDEX]))) {
426
GST_DEBUG_OBJECT (rtpasfpay, "Received data object header");
427
rtpasfpay->headers = gst_buffer_join (rtpasfpay->headers, buffer);
428
rtpasfpay->state = ASF_PACKETS;
430
return gst_rtp_asf_pay_parse_headers (rtpasfpay);
432
GST_ERROR_OBJECT (rtpasfpay, "Unexpected object received (was expecting "
434
gst_buffer_unref (buffer);
435
return GST_FLOW_ERROR;
439
if (G_LIKELY (rtpasfpay->state == ASF_PACKETS)) {
440
/* in broadcast mode we can't trust the packets count information
442
* We assume that if this is on broadcast mode it is a live stream
443
* and we are going to keep receiving packets indefinitely
445
if (rtpasfpay->asfinfo.broadcast ||
446
rtpasfpay->packets_count < rtpasfpay->asfinfo.packets_count) {
447
GST_DEBUG_OBJECT (rtpasfpay, "Received packet %"
448
G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
449
rtpasfpay->packets_count, rtpasfpay->asfinfo.packets_count);
450
rtpasfpay->packets_count++;
451
return gst_rtp_asf_pay_handle_packet (rtpasfpay, buffer);
453
GST_INFO_OBJECT (rtpasfpay, "Packets ended");
454
rtpasfpay->state = ASF_END;
455
gst_buffer_unref (buffer);
456
return GST_FLOW_UNEXPECTED;
460
gst_buffer_unref (buffer);
465
gst_rtp_asf_pay_plugin_init (GstPlugin * plugin)
467
return gst_element_register (plugin, "rtpasfpay",
468
GST_RANK_NONE, GST_TYPE_RTP_ASF_PAY);