asfmux: Adds new plugin asfmux

Adds the brand new asfmux plugin, containing 3 elements:
asfmux, rtpasfpay and asfparse. This plugin was developed
as a GSoC 2009 project, with David Schleef as the mentor and
Thiago Santos as the student.
This commit is contained in:
Thiago Santos 2009-07-24 14:52:28 -03:00
parent 30feab574a
commit 2641cd9d94
11 changed files with 4732 additions and 0 deletions

View file

@ -254,6 +254,7 @@ AG_GST_CHECK_PLUGIN(aacparse)
AG_GST_CHECK_PLUGIN(adpcmdec)
AG_GST_CHECK_PLUGIN(aiffparse)
AG_GST_CHECK_PLUGIN(amrparse)
AG_GST_CHECK_PLUGIN(asfmux)
AG_GST_CHECK_PLUGIN(autoconvert)
AG_GST_CHECK_PLUGIN(camerabin)
AG_GST_CHECK_PLUGIN(legacyresample)
@ -1626,6 +1627,7 @@ gst/aacparse/Makefile
gst/adpcmdec/Makefile
gst/aiffparse/Makefile
gst/amrparse/Makefile
gst/asfmux/Makefile
gst/autoconvert/Makefile
gst/bayer/Makefile
gst/camerabin/Makefile

24
gst/asfmux/Makefile.am Normal file
View file

@ -0,0 +1,24 @@
# plugindir is set in configure
plugin_LTLIBRARIES = libgstasfmux.la
# sources used to compile this plug-in
libgstasfmux_la_SOURCES = gstasfmux.c \
gstasfobjects.c \
gstasfparse.c \
gstrtpasfpay.c \
gstasf.c
# flags used to compile this plugin
# add other _CFLAGS and _LIBS as needed
libgstasfmux_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
libgstasfmux_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \
-lgstrtp-@GST_MAJORMINOR@
libgstasfmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
#libgstasfmux_la_LIBTOOLFLAGS = --tag=disable-static
# headers we need but don't want installed
noinst_HEADERS = gstasfmux.h \
gstasfobjects.h \
gstasfparse.h \
gstrtpasfpay.h

51
gst/asfmux/gstasf.c Normal file
View file

@ -0,0 +1,51 @@
/* GStreamer
* Copyright (C) 2009 Thiago Santos <thiagoss@embeddeed.ufcg.edu.br>
*
* gstasf.c: plugin registering
*
* 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
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include "gstasfmux.h"
#include "gstrtpasfpay.h"
#include "gstasfparse.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_asf_mux_plugin_init (plugin)) {
return FALSE;
}
if (!gst_rtp_asf_pay_plugin_init (plugin)) {
return FALSE;
}
if (!gst_asf_parse_plugin_init (plugin)) {
return FALSE;
}
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"asfmux",
"ASF Muxer Plugin",
plugin_init, VERSION, "LGPL", "gsoc2009 package", "embedded.ufcg.edu.br")

2259
gst/asfmux/gstasfmux.c Normal file

File diff suppressed because it is too large Load diff

159
gst/asfmux/gstasfmux.h Normal file
View file

@ -0,0 +1,159 @@
/* ASF muxer plugin for GStreamer
* Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* 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
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_ASF_MUX_H__
#define __GST_ASF_MUX_H__
#include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
#include <gst/riff/riff-media.h>
#include "gstasfobjects.h"
G_BEGIN_DECLS
#define GST_TYPE_ASF_MUX \
(gst_asf_mux_get_type())
#define GST_ASF_MUX(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASF_MUX,GstAsfMux))
#define GST_ASF_MUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASF_MUX,GstAsfMuxClass))
#define GST_IS_ASF_MUX(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASF_MUX))
#define GST_IS_ASF_MUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASF_MUX))
#define GST_ASF_MUX_CAST(obj) ((GstAsfMux*)(obj))
typedef struct _GstAsfMux GstAsfMux;
typedef struct _GstAsfMuxClass GstAsfMuxClass;
typedef struct _GstAsfPad GstAsfPad;
typedef struct _GstAsfAudioPad GstAsfAudioPad;
typedef struct _GstAsfVideoPad GstAsfVideoPad;
typedef enum _GstAsfMuxState GstAsfMuxState;
enum _GstAsfMuxState
{
GST_ASF_MUX_STATE_NONE,
GST_ASF_MUX_STATE_STARTED,
GST_ASF_MUX_STATE_HEADERS,
GST_ASF_MUX_STATE_DATA,
GST_ASF_MUX_STATE_EOS
};
struct _GstAsfPad
{
GstCollectData collect;
gboolean is_audio;
guint8 stream_number;
guint8 media_object_number;
guint32 bitrate;
GstClockTime play_duration;
GstBuffer *codec_data;
/* stream only metadata */
GstTagList *taglist;
};
struct _GstAsfAudioPad
{
GstAsfPad pad;
gst_riff_strf_auds audioinfo;
};
struct _GstAsfVideoPad
{
GstAsfPad pad;
gst_riff_strf_vids vidinfo;
/* Simple Index Entries */
GSList *simple_index;
gboolean has_keyframe; /* if we have received one at least */
guint32 last_keyframe_packet;
guint16 last_keyframe_packet_count;
guint16 max_keyframe_packet_count;
GstClockTime next_index_time;
guint64 time_interval;
};
struct _GstAsfMux
{
GstElement element;
/* output stream state */
GstAsfMuxState state;
/* counter to assign stream numbers */
guint8 stream_number;
/* counting variables */
guint64 file_size;
guint64 data_object_size;
guint64 total_data_packets;
/*
* data object size field position
* needed for updating when finishing the file
*/
guint64 data_object_position;
guint64 file_properties_object_position;
/* payloads still to be sent in a packet */
guint32 payload_data_size;
GSList *payloads;
Guid file_id;
/* properties */
guint32 prop_packet_size;
guint64 prop_preroll;
gboolean prop_merge_stream_tags;
guint64 prop_padding;
gboolean prop_is_live;
/* same as properties, but those are stored here to be
* used without modification while muxing a single file */
guint32 packet_size;
guint64 preroll; /* milisecs */
gboolean merge_stream_tags;
/* pads */
GstPad *srcpad;
/* sinkpads, video first */
GSList *sinkpads;
GstCollectPads *collect;
GstPadEventFunction collect_event;
};
struct _GstAsfMuxClass
{
GstElementClass parent_class;
};
GType gst_asf_mux_get_type (void);
gboolean gst_asf_mux_plugin_init (GstPlugin * plugin);
G_END_DECLS
#endif /* __GST_ASF_MUX_H__ */

803
gst/asfmux/gstasfobjects.c Normal file
View file

@ -0,0 +1,803 @@
/* ASF muxer plugin for GStreamer
* Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* 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
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "gstasfobjects.h"
#include <string.h>
/* Guids */
const Guid guids[] = {
/* asf header object */
{0x75B22630, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)},
/* asf file properties object */
{0x8CABDCA1, 0xA947, 0x11CF, G_GUINT64_CONSTANT (0x8EE400C00C205365)},
/* asf stream properties object */
{0xB7DC0791, 0xA9B7, 0x11CF, G_GUINT64_CONSTANT (0x8EE600C00C205365)},
/* asf audio media */
{0xF8699E40, 0x5B4D, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)},
/* asf no error correction */
{0x20FB5700, 0x5B55, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)},
/* asf audio spread */
{0xBFC3CD50, 0x618F, 0x11CF, G_GUINT64_CONSTANT (0x8BB200AA00B4E220)},
/* asf header extension object */
{0x5FBF03B5, 0xA92E, 0x11CF, G_GUINT64_CONSTANT (0x8EE300C00C205365)},
/* asf reserved 1 */
{0xABD3D211, 0xA9BA, 0x11CF, G_GUINT64_CONSTANT (0x8EE600C00C205365)},
/* asf data object */
{0x75B22636, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)},
/* asf extended stream properties object */
{0x14E6A5CB, 0xC672, 0x4332, G_GUINT64_CONSTANT (0x8399A96952065B5A)},
/* asf video media */
{0xBC19EFC0, 0x5B4D, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)},
/* asf simple index object */
{0x33000890, 0xE5B1, 0x11CF, G_GUINT64_CONSTANT (0x89F400A0C90349CB)},
/* asf content description */
{0x75B22633, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)},
/* asf extended content description */
{0xD2D0A440, 0xE307, 0x11D2, G_GUINT64_CONSTANT (0x97F000A0C95EA850)},
/* asf metadata object */
{0xC5F8CBEA, 0x5BAF, 0x4877, G_GUINT64_CONSTANT (0x8467AA8C44FA4CCA)},
/* asf padding object */
{0x1806D474, 0xCADF, 0x4509, G_GUINT64_CONSTANT (0xA4BA9AABCB96AAE8)}
};
/**
* gst_asf_generate_file_id:
*
* Generates a random GUID
*
* Returns: The generated GUID
*/
Guid
gst_asf_generate_file_id ()
{
guint32 aux;
Guid guid;
guid.v1 = g_random_int ();
aux = g_random_int ();
guid.v2 = (guint16) (aux & 0x0000FFFF);
guid.v3 = (guint16) (aux >> 16);
guid.v4 = (((guint64) g_random_int ()) << 32) | (guint64) g_random_int ();
return guid;
}
/**
* gst_byte_reader_get_asf_var_size_field:
* @reader: A #GstByteReader
* @field_type: an asf field type
* @var: pointer to store the result
*
* Reads the proper data from the #GstByteReader according to the
* asf field type and stores it in var
*
* Returns: True on success, false otherwise
*/
gboolean
gst_byte_reader_get_asf_var_size_field (GstByteReader * reader,
guint8 field_type, guint32 * var)
{
guint8 aux8 = 0;
guint16 aux16 = 0;
guint32 aux32 = 0;
gboolean ret;
switch (field_type) {
case ASF_FIELD_TYPE_DWORD:
ret = gst_byte_reader_get_uint32_le (reader, &aux32);
*var = aux32;
break;
case ASF_FIELD_TYPE_WORD:
ret = gst_byte_reader_get_uint16_le (reader, &aux16);
*var = aux16;
break;
case ASF_FIELD_TYPE_BYTE:
ret = gst_byte_reader_get_uint8 (reader, &aux8);
*var = aux8;
break;
case ASF_FIELD_TYPE_NONE:
ret = TRUE;
*var = 0;
break;
default:
return FALSE;
}
return ret;
}
/**
* gst_asf_read_var_size_field:
* @data: pointer to the data to be read
* @field_type: the asf field type pointed by data
*
* Reads and returns the value read from the data, according to the
* field type given
*
* Returns: The value read
*/
guint32
gst_asf_read_var_size_field (guint8 * data, guint8 field_type)
{
switch (field_type) {
case ASF_FIELD_TYPE_DWORD:
return GST_READ_UINT32_LE (data);
case ASF_FIELD_TYPE_WORD:
return GST_READ_UINT16_LE (data);
case ASF_FIELD_TYPE_BYTE:
return data[0];
default:
return 0;
}
}
/**
* gst_asf_get_var_size_field_len:
* @field_type: the asf field type
*
* Returns: the size in bytes of a variable of field_type type
*/
guint
gst_asf_get_var_size_field_len (guint8 field_type)
{
switch (field_type) {
case ASF_FIELD_TYPE_DWORD:
return 4;
case ASF_FIELD_TYPE_WORD:
return 2;
case ASF_FIELD_TYPE_BYTE:
return 1;
default:
return 0;
}
}
/**
* gst_asf_file_info_new:
* Creates a new #GstAsfFileInfo
* Returns: the created struct
*/
GstAsfFileInfo *
gst_asf_file_info_new ()
{
return g_new0 (GstAsfFileInfo, 1);
}
/**
* gst_asf_file_info_reset:
* @info: the #GstAsfFileInfo to be reset
* resets the data of a #GstFileInfo
*/
void
gst_asf_file_info_reset (GstAsfFileInfo * info)
{
info->packet_size = 0;
info->packets_count = 0;
info->broadcast = FALSE;
}
/**
* gst_asf_file_info_free:
* @info: the #GstAsfFileInfo to be freed
*
* Releases memory associated with this #GstAsfFileInfo
*/
void
gst_asf_file_info_free (GstAsfFileInfo * info)
{
g_free (info);
}
/**
* gst_asf_payload_get_size:
* @payload: the payload to get the size from
*
* Returns: the size of an asf payload of the data represented by this
* #AsfPayload
*/
guint32
gst_asf_payload_get_size (AsfPayload * payload)
{
return ASF_MULTIPLE_PAYLOAD_HEADER_SIZE + GST_BUFFER_SIZE (payload->data);
}
/**
* gst_asf_payload_free:
* @payload: the #AsfPayload to be freed
*
* Releases teh memory associated with this payload
*/
void
gst_asf_payload_free (AsfPayload * payload)
{
gst_buffer_unref (payload->data);
g_free (payload);
}
/**
* gst_asf_get_current_time:
*
* Gets system current time in ASF time unit
* (100-nanoseconds since Jan, 1st 1601)
*
* Returns:
*/
guint64
gst_asf_get_current_time ()
{
GTimeVal timeval;
guint64 secs;
guint64 usecs;
g_get_current_time (&timeval);
secs = (guint64) timeval.tv_sec;
usecs = (guint64) timeval.tv_usec;
return secs * G_GUINT64_CONSTANT (10000000) + usecs * 10
+ G_GUINT64_CONSTANT (116444628000000000);
}
/**
* gst_asf_match_guid:
* @data: pointer to the guid to be tested
* @guid: guid to match against data
*
* Checks if the guid pointed by data is the same
* as the guid parameter
*
* Returns: True if they are the same, false otherwise
*/
gboolean
gst_asf_match_guid (const guint8 * data, const Guid * guid)
{
Guid g;
g.v1 = GST_READ_UINT32_LE (data);
g.v2 = GST_READ_UINT16_LE (data + 4);
g.v3 = GST_READ_UINT16_LE (data + 6);
g.v4 = GST_READ_UINT64_BE (data + 8);
return g.v1 == guid->v1 &&
g.v2 == guid->v2 && g.v3 == guid->v3 && g.v4 == guid->v4;
}
/**
* gst_asf_put_i32:
* @buf: the memory to write data to
* @data: the value to be writen
*
* Writes a 32 bit signed integer to memory
*/
void
gst_asf_put_i32 (guint8 * buf, gint32 data)
{
*(gint32 *) buf = data;
}
/**
* gst_asf_put_time:
* @buf: pointer to the buffer to write the value to
* @time: value to be writen
*
* Writes an asf time value to the buffer
*/
void
gst_asf_put_time (guint8 * buf, guint64 time)
{
GST_WRITE_UINT64_LE (buf, time);
}
/**
* gst_asf_put_guid:
* @buf: the buffer to write the guid to
* @guid: the guid to be writen
*
* Writes a GUID to the buffer
*/
void
gst_asf_put_guid (guint8 * buf, Guid guid)
{
guint32 *aux32 = (guint32 *) buf;
guint16 *aux16 = (guint16 *) & (buf[4]);
guint64 *aux64 = (guint64 *) & (buf[8]);
*aux32 = GUINT32_TO_LE (guid.v1);
*aux16 = GUINT16_TO_LE (guid.v2);
aux16 = (guint16 *) & (buf[6]);
*aux16 = GUINT16_TO_LE (guid.v3);
*aux64 = GUINT64_TO_BE (guid.v4);
}
/**
* gst_asf_put_payload:
* @buf: memory to write the payload to
* @payload: #AsfPayload to be writen
*
* Writes the asf payload to the buffer. The #AsfPayload
* packet count is incremented.
*/
void
gst_asf_put_payload (guint8 * buf, AsfPayload * payload)
{
GST_WRITE_UINT8 (buf, payload->stream_number);
GST_WRITE_UINT8 (buf + 1, payload->media_obj_num);
GST_WRITE_UINT32_LE (buf + 2, payload->offset_in_media_obj);
GST_WRITE_UINT8 (buf + 6, payload->replicated_data_length);
GST_WRITE_UINT32_LE (buf + 7, payload->media_object_size);
GST_WRITE_UINT32_LE (buf + 11, payload->presentation_time);
GST_WRITE_UINT16_LE (buf + 15, (guint16) GST_BUFFER_SIZE (payload->data));
memcpy (buf + 17, GST_BUFFER_DATA (payload->data),
GST_BUFFER_SIZE (payload->data));
payload->packet_count++;
}
/**
* gst_asf_put_subpayload:
* @buf: buffer to write the payload to
* @payload: the payload to be writen
* @size: maximum size in bytes to write
*
* Serializes part of a payload to a buffer.
* The maximum size is checked against the payload length,
* the minimum of this size and the payload length is writen
* to the buffer and the writen size is returned.
*
* It also updates the values of the payload to match the remaining
* data.
* In case there is not enough space to write the headers, nothing is done.
*
* Returns: The writen size in bytes.
*/
guint16
gst_asf_put_subpayload (guint8 * buf, AsfPayload * payload, guint16 size)
{
guint16 payload_size;
GstBuffer *newbuf;
if (size <= ASF_MULTIPLE_PAYLOAD_HEADER_SIZE) {
return 0; /* do nothing if there is not enough space */
}
GST_WRITE_UINT8 (buf, payload->stream_number);
GST_WRITE_UINT8 (buf + 1, payload->media_obj_num);
GST_WRITE_UINT32_LE (buf + 2, payload->offset_in_media_obj);
GST_WRITE_UINT8 (buf + 6, payload->replicated_data_length);
GST_WRITE_UINT32_LE (buf + 7, payload->media_object_size);
GST_WRITE_UINT32_LE (buf + 11, payload->presentation_time);
size -= ASF_MULTIPLE_PAYLOAD_HEADER_SIZE;
payload_size = size < GST_BUFFER_SIZE (payload->data) ?
size : GST_BUFFER_SIZE (payload->data);
GST_WRITE_UINT16_LE (buf + 15, payload_size);
memcpy (buf + 17, GST_BUFFER_DATA (payload->data), payload_size);
/* updates the payload to the remaining data */
payload->offset_in_media_obj += payload_size;
newbuf = gst_buffer_create_sub (payload->data, payload_size,
GST_BUFFER_SIZE (payload->data) - payload_size);
gst_buffer_copy_metadata (payload->data, newbuf, GST_BUFFER_COPY_FLAGS |
GST_BUFFER_COPY_CAPS);
GST_BUFFER_TIMESTAMP (newbuf) = GST_BUFFER_TIMESTAMP (payload->data);
gst_buffer_unref (payload->data);
payload->data = newbuf;
payload->packet_count++;
return payload_size;
}
/**
* gst_asf_match_and_peek_obj_size:
* @data: data to be peeked at
* @guid: pointer to a guid
*
* Compares the first bytes of data against the guid parameter and
* if they match gets the object size (that are right after the guid in
* asf objects).
*
* In case the guids don't match, 0 is returned.
* If the guid is NULL the match is assumed to be true.
*
* Returns: The size of the object in case the guid matches, 0 otherwise
*/
guint64
gst_asf_match_and_peek_obj_size (const guint8 * data, const Guid * guid)
{
g_assert (data);
if (guid && !gst_asf_match_guid (data, guid)) {
/* this is not the expected object */
return 0;
}
/* return the object size */
return GST_READ_UINT64_LE (data + ASF_GUID_SIZE);
}
/**
* gst_asf_parse_mult_payload:
* @reader: a #GstByteReader ready to read the multiple payload data
* @has_keyframe: pointer to return the result
*
* Parses a multiple payload section of an asf data packet
* to see if any of the paylaods has a a keyframe
*
* Notice that the #GstByteReader might not be positioned after
* this section on this function return. Because this section
* is the last one in an asf packet and the remaining data
* is probably uninteresting to the application.
*
* Returns: true on success, false if some error occurrs
*/
static gboolean
gst_asf_parse_mult_payload (GstByteReader * reader, gboolean * has_keyframe)
{
guint payloads;
guint8 payload_len_type;
guint8 rep_data_len;
guint32 payload_len;
guint8 stream_num;
guint8 aux;
guint i;
if (!gst_byte_reader_get_uint8 (reader, &aux))
return FALSE;
payloads = (aux & 0x3F);
payload_len_type = (aux & 0xC0) >> 6;
*has_keyframe = FALSE;
for (i = 0; i < payloads; i++) {
GST_LOG ("Parsing payload %u/%u", i + 1, payloads);
if (!gst_byte_reader_get_uint8 (reader, &stream_num))
goto error;
if ((stream_num & 0x80) != 0) {
GST_LOG ("Keyframe found, stoping parse of payloads");
*has_keyframe = TRUE;
return TRUE;
}
/* skip to replicated data length */
if (!gst_byte_reader_skip (reader, 5))
goto error;
if (!gst_byte_reader_get_uint8 (reader, &rep_data_len))
goto error;
if (!gst_byte_reader_skip (reader, rep_data_len))
goto error;
if (!gst_byte_reader_get_asf_var_size_field (reader, payload_len_type,
&payload_len))
goto error;
if (!gst_byte_reader_skip (reader, payload_len))
goto error;
}
/* we do not skip the rest of the payload bytes as
this is the last data to be parsed on the buffer */
return TRUE;
error:
GST_WARNING ("Error while parsing payloads");
return FALSE;
}
/**
* gst_asf_parse_single_payload:
* @reader: a #GstByteReader ready to read the multiple payload data
* @has_keyframe: pointer to return the result
*
* Parses a single payload section of an asf data packet
* to see if any of the paylaods has a a keyframe
*
* Notice that the #GstByteReader might not be positioned after
* this section on this function return. Because this section
* is the last one in an asf packet and the remaining data
* is probably uninteresting to the application.
*
* Returns: true on success, false if some error occurrs
*/
static gboolean
gst_asf_parse_single_payload (GstByteReader * reader, gboolean * has_keyframe)
{
guint8 stream_num;
if (!gst_byte_reader_get_uint8 (reader, &stream_num))
return GST_FLOW_ERROR;
*has_keyframe = (stream_num & 0x80) != 0;
/* we do not skip the rest of the payload bytes as
this is the last data to be parsed on the buffer */
return TRUE;
}
gboolean
gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet,
gboolean trust_delta_flag)
{
GstByteReader *reader;
gboolean ret = TRUE;
guint8 first;
guint8 aux;
guint8 packet_len_type;
guint8 padding_len_type;
guint8 seq_len_type;
guint8 rep_data_len_type;
guint8 mo_number_len_type;
guint8 mo_offset_type;
gboolean mult_payloads;
guint32 packet_len;
guint32 padd_len;
guint32 send_time;
guint16 duration;
gboolean has_keyframe;
reader = gst_byte_reader_new_from_buffer (buffer);
GST_LOG ("Starting packet parsing, size: %u", GST_BUFFER_SIZE (buffer));
if (!gst_byte_reader_get_uint8 (reader, &first))
goto error;
if (first & 0x80) { /* error correction present */
guint8 err_cor_len;
GST_DEBUG ("Packet contains error correction");
if (first & 0x60) {
GST_ERROR ("Error correction data length should be "
"set to 0 and is reserved for future use.");
return FALSE;
}
err_cor_len = (first & 0x0F);
GST_DEBUG ("Error correction data length: %d", (gint) err_cor_len);
if (!gst_byte_reader_skip (reader, err_cor_len))
goto error;
if (!gst_byte_reader_get_uint8 (reader, &aux))
goto error;
} else {
aux = first;
}
mult_payloads = (aux & 0x1) != 0;
packet_len_type = (aux >> 5) & 0x3;
padding_len_type = (aux >> 3) & 0x3;
seq_len_type = (aux >> 1) & 0x3;
GST_LOG ("Field sizes: packet length type: %u "
", padding length type: %u, sequence length type: %u",
gst_asf_get_var_size_field_len (packet_len_type),
gst_asf_get_var_size_field_len (padding_len_type),
gst_asf_get_var_size_field_len (seq_len_type));
if (mult_payloads) {
GST_DEBUG ("Packet contains multiple payloads");
}
if (!gst_byte_reader_get_uint8 (reader, &aux))
goto error;
rep_data_len_type = aux & 0x3;
mo_offset_type = (aux >> 2) & 0x3;
mo_number_len_type = (aux >> 4) & 0x3;
/* gets the fields lengths */
GST_LOG ("Getting packet and padding length");
if (!gst_byte_reader_get_asf_var_size_field (reader,
packet_len_type, &packet_len))
goto error;
if (!gst_byte_reader_skip (reader,
gst_asf_get_var_size_field_len (seq_len_type)))
goto error;
if (!gst_byte_reader_get_asf_var_size_field (reader,
padding_len_type, &padd_len))
goto error;
if (packet_len_type != ASF_FIELD_TYPE_NONE &&
packet_len != GST_BUFFER_SIZE (buffer)) {
GST_WARNING ("ASF packets should be aligned with buffers");
ret = FALSE;
goto end;
}
GST_LOG ("Getting send time and duration");
if (!gst_byte_reader_get_uint32_le (reader, &send_time))
goto error;
if (!gst_byte_reader_get_uint16_le (reader, &duration))
goto error;
has_keyframe = FALSE;
GST_LOG ("Checking for keyframes");
if (trust_delta_flag) {
has_keyframe = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
} else {
if (mult_payloads) {
ret = gst_asf_parse_mult_payload (reader, &has_keyframe);
} else {
ret = gst_asf_parse_single_payload (reader, &has_keyframe);
}
}
if (!ret) {
GST_WARNING ("Failed to parse payloads");
goto end;
}
GST_DEBUG ("Received packet of length %" G_GUINT32_FORMAT
", padding %" G_GUINT32_FORMAT ", send time %" G_GUINT32_FORMAT
", duration %" G_GUINT16_FORMAT " and %s keyframe(s)",
packet_len, padd_len, send_time, duration,
(has_keyframe) ? "with" : "without");
packet->packet_size = packet_len;
packet->padding = padd_len;
packet->send_time = send_time;
packet->duration = duration;
packet->has_keyframe = has_keyframe;
gst_byte_reader_free (reader);
return ret;
error:
ret = FALSE;
GST_WARNING ("Error while parsing data packet");
end:
gst_byte_reader_free (reader);
return ret;
}
static gboolean
gst_asf_parse_file_properties_obj (GstByteReader * reader,
GstAsfFileInfo * asfinfo)
{
guint32 min_ps;
guint32 max_ps;
guint64 packets;
guint32 flags;
GST_DEBUG ("ASF: Parsing file properties object");
/* skip until data packets count */
if (!gst_byte_reader_skip (reader, 32))
return FALSE;
if (!gst_byte_reader_get_uint64_le (reader, &packets))
return FALSE;
asfinfo->packets_count = packets;
GST_DEBUG ("ASF: packets count %" G_GUINT64_FORMAT, packets);
/* skip until flags */
if (!gst_byte_reader_skip (reader, 24))
return FALSE;
if (!gst_byte_reader_get_uint32_le (reader, &flags))
return GST_FLOW_ERROR;
asfinfo->broadcast = (flags & 0x1) == 1;
GST_DEBUG ("ASF: broadcast flag: %s", asfinfo->broadcast ? "true" : "false");
if (!gst_byte_reader_get_uint32_le (reader, &min_ps))
return GST_FLOW_ERROR;
if (!gst_byte_reader_get_uint32_le (reader, &max_ps))
return GST_FLOW_ERROR;
if (min_ps != max_ps) {
GST_WARNING ("Mininum and maximum packet size differ "
"%" G_GUINT32_FORMAT " and %" G_GUINT32_FORMAT ", "
"ASF spec states they should be the same", min_ps, max_ps);
return FALSE;
}
GST_DEBUG ("ASF: Packet size: %" G_GUINT32_FORMAT, min_ps);
asfinfo->packet_size = min_ps;
if (!gst_byte_reader_skip (reader, 4))
return FALSE;
return TRUE;
}
gboolean
gst_asf_parse_headers (GstBuffer * buffer, GstAsfFileInfo * file_info)
{
gboolean ret = TRUE;
guint32 header_objects;
guint32 i;
GstByteReader *reader;
guint64 object_size;
object_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buffer),
&(guids[ASF_HEADER_OBJECT_INDEX]));
if (object_size == 0) {
GST_WARNING ("ASF: Cannot parse, header guid not found at the beginning "
" of data");
return FALSE;
}
reader = gst_byte_reader_new_from_buffer (buffer);
if (!gst_byte_reader_skip (reader, ASF_GUID_OBJSIZE_SIZE))
goto error;
if (!gst_byte_reader_get_uint32_le (reader, &header_objects))
goto error;
GST_DEBUG ("ASF: Header has %" G_GUINT32_FORMAT " child"
" objects", header_objects);
/* skip reserved bytes */
if (!gst_byte_reader_skip (reader, 2))
goto error;
/* iterate through childs of header object */
for (i = 0; i < header_objects; i++) {
const guint8 *guid = NULL;
guint64 obj_size;
if (!gst_byte_reader_get_data (reader, ASF_GUID_SIZE, &guid))
goto error;
if (!gst_byte_reader_get_uint64_le (reader, &obj_size))
goto error;
if (gst_asf_match_guid (guid, &guids[ASF_FILE_PROPERTIES_OBJECT_INDEX])) {
ret = gst_asf_parse_file_properties_obj (reader, file_info);
} else {
/* we don't know/care about this object */
if (!gst_byte_reader_skip (reader, obj_size - ASF_GUID_OBJSIZE_SIZE))
goto error;
}
if (!ret)
goto end;
}
goto end;
error:
ret = FALSE;
GST_WARNING ("ASF: Error while parsing headers");
end:
gst_byte_reader_free (reader);
return ret;
}
#define MAP_GST_TO_ASF_TAG(tag, gst, asf) \
if (strcmp (tag, gst) == 0) \
return asf
/**
* gst_asf_get_asf_tag:
* @gsttag: a gstreamer tag
*
* Maps gstreamer tags to asf tags
*
* Returns: The tag corresponding name in asf files or NULL if it is not mapped
*/
const gchar *
gst_asf_get_asf_tag (const gchar * gsttag)
{
g_return_val_if_fail (gsttag != NULL, NULL);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TITLE, ASF_TAG_TITLE);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TITLE_SORTNAME, ASF_TAG_TITLE_SORTNAME);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ARTIST, ASF_TAG_ARTIST);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ARTIST_SORTNAME, ASF_TAG_ARTIST_SORTNAME);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ALBUM, ASF_TAG_ALBUM_TITLE);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ALBUM_SORTNAME,
ASF_TAG_ALBUM_TITLE_SORTNAME);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_GENRE, ASF_TAG_GENRE);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COPYRIGHT, ASF_TAG_COPYRIGHT);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COMPOSER, ASF_TAG_COMPOSER);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COMMENT, ASF_TAG_COMMENT);
MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TRACK_NUMBER, ASF_TAG_TRACK_NUMBER);
return NULL;
}
guint
gst_asf_get_tag_field_type (GValue * value)
{
if (G_VALUE_HOLDS_STRING (value))
return ASF_TAG_TYPE_UNICODE_STR;
if (G_VALUE_HOLDS_UINT (value))
return ASF_TAG_TYPE_DWORD;
return -1;
}
gboolean
gst_asf_tag_present_in_content_description (const gchar * tag)
{
return strcmp (tag, GST_TAG_TITLE) == 0 ||
strcmp (tag, GST_TAG_ARTIST) == 0 ||
strcmp (tag, GST_TAG_COPYRIGHT) == 0 ||
strcmp (tag, GST_TAG_DESCRIPTION) == 0;
/* FIXME we have no tag for rating */
}

190
gst/asfmux/gstasfobjects.h Normal file
View file

@ -0,0 +1,190 @@
/* ASF muxer plugin for GStreamer
* Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* 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
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_ASF_OBJECTS_H__
#define __GST_ASF_OBJECTS_H__
#include <glib.h>
#include <gst/gst.h>
#include <gst/base/gstbytereader.h>
#include <gst/base/gstcollectpads.h>
#define ASF_PAYLOAD_IS_KEYFRAME(pay) ((pay->stream_number & 0x80) != 0)
#define ASF_MILI_TO_100NANO(v) (v * 10000)
#define ASF_GUID_SIZE 16
#define ASF_GUID_OBJSIZE_SIZE 24
typedef struct _Guid
{
guint32 v1;
guint16 v2;
guint16 v3;
guint64 v4;
} Guid;
typedef struct _GstAsfFileInfo
{
guint64 packets_count;
guint32 packet_size;
gboolean broadcast;
} GstAsfFileInfo;
typedef struct _GstAsfPacketInfo
{
guint32 packet_size;
guint32 padding;
guint32 send_time;
guint16 duration;
gboolean has_keyframe;
} GstAsfPacketInfo;
typedef struct _SimpleIndexEntry
{
guint32 packet_number;
guint16 packet_count;
} SimpleIndexEntry;
typedef struct _AsfPayload
{
guint8 stream_number;
guint8 media_obj_num;
guint32 offset_in_media_obj;
guint8 replicated_data_length;
guint32 media_object_size;
guint32 presentation_time;
GstBuffer *data;
GstCollectData *pad;
/* simple index info */
gboolean has_packet_info;
guint32 packet_number;
guint16 packet_count;
} AsfPayload;
Guid gst_asf_generate_file_id ();
gboolean gst_byte_reader_get_asf_var_size_field (GstByteReader * reader,
guint8 field_type, guint32 * var);
guint32 gst_asf_read_var_size_field (guint8 * data, guint8 field_type);
guint gst_asf_get_var_size_field_len (guint8 field_type);
GstAsfFileInfo *gst_asf_file_info_new ();
void gst_asf_file_info_reset (GstAsfFileInfo * info);
void gst_asf_file_info_free (GstAsfFileInfo * info);
guint32 gst_asf_payload_get_size (AsfPayload * payload);
void gst_asf_payload_free (AsfPayload * payload);
guint64 gst_asf_get_current_time ();
gboolean gst_asf_match_guid (const guint8 * data, const Guid * g);
void gst_asf_put_i32 (guint8 * buf, gint32 data);
void gst_asf_put_time (guint8 * buf, guint64 time);
void gst_asf_put_guid (guint8 * buf, Guid guid);
void gst_asf_put_payload (guint8 * buf, AsfPayload * payload);
guint16 gst_asf_put_subpayload (guint8 * buf, AsfPayload * payload,
guint16 size);
gboolean gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet,
gboolean trust_delta_flag);
guint64 gst_asf_match_and_peek_obj_size (const guint8 * data,
const Guid * guid);
gboolean gst_asf_parse_headers (GstBuffer * buffer, GstAsfFileInfo * file_info);
/* ASF tags
* found at http://msdn.microsoft.com/en-us/library/dd562330(VS.85).aspx
*/
#define ASF_TAG_TITLE "Title\0"
#define ASF_TAG_TITLE_SORTNAME "TitleSortOrder\0"
/* FIXME asf has no artist tag other than AlbumArtist, but it has Author
* What to use here? */
#define ASF_TAG_ARTIST "WM/AlbumArtist\0"
#define ASF_TAG_ARTIST_SORTNAME "AlbumArtistSortOrder\0"
#define ASF_TAG_ALBUM_TITLE "WM/AlbumTitle\0"
#define ASF_TAG_ALBUM_TITLE_SORTNAME "AlbumTitleSortOrder\0"
#define ASF_TAG_GENRE "WM/Genre\0"
#define ASF_TAG_COMMENT "Comment\0"
#define ASF_TAG_TRACK_NUMBER "WM/TrackNumber\0"
#define ASF_TAG_COPYRIGHT "Copyright\0"
#define ASF_TAG_COMPOSER "WM/Composer\0"
const gchar *gst_asf_get_asf_tag (const gchar * gsttag);
guint gst_asf_get_tag_field_type (GValue * value);
gboolean gst_asf_tag_present_in_content_description (const gchar * tag);
/* ASF Objects Sizes */
#define ASF_HEADER_OBJECT_SIZE 30
#define ASF_FILE_PROPERTIES_OBJECT_SIZE 104
#define ASF_STREAM_PROPERTIES_OBJECT_SIZE 78
#define ASF_HEADER_EXTENSION_OBJECT_SIZE 46
#define ASF_AUDIO_SPECIFIC_DATA_SIZE 18
#define ASF_VIDEO_SPECIFIC_DATA_SIZE 51
#define ASF_DATA_OBJECT_SIZE 50
#define ASF_PAYLOAD_PARSING_INFO_SIZE 16
#define ASF_SINGLE_PAYLOAD_HEADER_SIZE 15
#define ASF_MULTIPLE_PAYLOAD_HEADER_SIZE 17
#define ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE 88
#define ASF_CONTENT_DESCRIPTION_OBJECT_SIZE 34
#define ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE 26
#define ASF_SIMPLE_INDEX_OBJECT_SIZE 56
#define ASF_SIMPLE_INDEX_ENTRY_SIZE 6
#define ASF_METADATA_OBJECT_SIZE 26
#define ASF_PADDING_OBJECT_SIZE 24
/* Field types for data object payload description */
#define ASF_FIELD_TYPE_NONE 0
#define ASF_FIELD_TYPE_BYTE 1
#define ASF_FIELD_TYPE_WORD 2
#define ASF_FIELD_TYPE_DWORD 3
/* tag types */
#define ASF_TAG_TYPE_UNICODE_STR 0
#define ASF_TAG_TYPE_BYTES 1
#define ASF_TAG_TYPE_BOOL 2
#define ASF_TAG_TYPE_DWORD 3
#define ASF_TAG_TYPE_QWORD 4
#define ASF_TAG_TYPE_WORD 5
/* GUID objects */
#define ASF_HEADER_OBJECT_INDEX 0
#define ASF_FILE_PROPERTIES_OBJECT_INDEX 1
#define ASF_STREAM_PROPERTIES_OBJECT_INDEX 2
#define ASF_AUDIO_MEDIA_INDEX 3
#define ASF_NO_ERROR_CORRECTION_INDEX 4
#define ASF_AUDIO_SPREAD_INDEX 5
#define ASF_HEADER_EXTENSION_OBJECT_INDEX 6
#define ASF_RESERVED_1_INDEX 7
#define ASF_DATA_OBJECT_INDEX 8
#define ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_INDEX 9
#define ASF_VIDEO_MEDIA_INDEX 10
#define ASF_SIMPLE_INDEX_OBJECT_INDEX 11
#define ASF_CONTENT_DESCRIPTION_INDEX 12
#define ASF_EXT_CONTENT_DESCRIPTION_INDEX 13
#define ASF_METADATA_OBJECT_INDEX 14
#define ASF_PADDING_OBJECT_INDEX 15
extern const Guid guids[];
#endif

623
gst/asfmux/gstasfparse.c Normal file
View file

@ -0,0 +1,623 @@
/* ASF parser plugin for GStreamer
* Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* 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
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstasfparse.h"
/* FIXME add this include
* #include <gst/gst-i18n-plugin.h> */
GST_DEBUG_CATEGORY_STATIC (asfparse_debug);
#define GST_CAT_DEFAULT asfparse_debug
enum
{
PROP_0,
};
static const GstElementDetails gst_asf_parse_details =
GST_ELEMENT_DETAILS ("ASF parser",
"Parser",
"Parses ASF",
"Thiago Santos <thiagoss@embedded.ufcg.edu.br>");
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-ms-asf, parsed = (boolean) true")
);
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-ms-asf, parsed = (boolean) false")
);
static void gst_asf_parse_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_asf_parse_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static GstStateChangeReturn gst_asf_parse_change_state (GstElement * element,
GstStateChange transition);
static void gst_asf_parse_loop (GstPad * pad);
GST_BOILERPLATE (GstAsfParse, gst_asf_parse, GstElement, GST_TYPE_ELEMENT);
static void
gst_asf_parse_reset (GstAsfParse * asfparse)
{
gst_adapter_clear (asfparse->adapter);
gst_asf_file_info_reset (asfparse->asfinfo);
asfparse->parse_state = ASF_PARSING_HEADERS;
asfparse->headers_size = 0;
asfparse->data_size = 0;
asfparse->parsed_packets = 0;
asfparse->offset = 0;
}
static gboolean
gst_asf_parse_sink_activate (GstPad * pad)
{
if (gst_pad_check_pull_range (pad)) {
return gst_pad_activate_pull (pad, TRUE);
} else {
return gst_pad_activate_push (pad, TRUE);
}
}
static gboolean
gst_asf_parse_sink_activate_pull (GstPad * pad, gboolean active)
{
if (active) {
return gst_pad_start_task (pad, (GstTaskFunction) gst_asf_parse_loop, pad);
} else {
return gst_pad_stop_task (pad);
}
}
static GstFlowReturn
gst_asf_parse_push (GstAsfParse * asfparse, GstBuffer * buf)
{
gst_buffer_set_caps (buf, asfparse->outcaps);
return gst_pad_push (asfparse->srcpad, buf);
}
static GstFlowReturn
gst_asf_parse_parse_data_object (GstAsfParse * asfparse, GstBuffer * buffer)
{
GstByteReader *reader;
GstFlowReturn ret = GST_FLOW_OK;
guint64 packet_count;
GST_DEBUG_OBJECT (asfparse, "Parsing data object");
reader = gst_byte_reader_new_from_buffer (buffer);
/* skip to packet count */
if (!gst_byte_reader_skip (reader, 40))
goto error;
if (!gst_byte_reader_get_uint64_le (reader, &packet_count))
goto error;
if (asfparse->asfinfo->packets_count != packet_count) {
GST_WARNING_OBJECT (asfparse, "File properties object and data object have "
"different packets count, %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT,
asfparse->asfinfo->packets_count, packet_count);
} else {
GST_DEBUG_OBJECT (asfparse, "Total packets: %" G_GUINT64_FORMAT,
packet_count);
}
gst_byte_reader_free (reader);
return gst_asf_parse_push (asfparse, buffer);
error:
ret = GST_FLOW_ERROR;
GST_ERROR_OBJECT (asfparse, "Error while parsing data object headers");
gst_byte_reader_free (reader);
return ret;
}
static GstFlowReturn
gst_asf_parse_parse_packet (GstAsfParse * asfparse, GstBuffer * buffer)
{
GstAsfPacketInfo *packetinfo = asfparse->packetinfo;
if (!gst_asf_parse_packet (buffer, packetinfo, FALSE))
goto error;
GST_DEBUG_OBJECT (asfparse, "Received packet of length %" G_GUINT32_FORMAT
", padding %" G_GUINT32_FORMAT ", send time %" G_GUINT32_FORMAT
", duration %" G_GUINT16_FORMAT " and %s keyframe(s)",
packetinfo->packet_size, packetinfo->padding,
packetinfo->send_time, packetinfo->duration,
(packetinfo->has_keyframe) ? "with" : "without");
/* set gstbuffer fields */
if (!packetinfo->has_keyframe) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
}
GST_BUFFER_TIMESTAMP (buffer) = ((GstClockTime) packetinfo->send_time)
* GST_MSECOND;
GST_BUFFER_DURATION (buffer) = ((GstClockTime) packetinfo->duration)
* GST_MSECOND;
return gst_asf_parse_push (asfparse, buffer);
error:
GST_ERROR_OBJECT (asfparse, "Error while parsing data packet");
return GST_FLOW_ERROR;
}
static GstFlowReturn
gst_asf_parse_pull_headers (GstAsfParse * asfparse)
{
GstBuffer *guid_and_size = NULL;
GstBuffer *headers = NULL;
guint64 size;
GstFlowReturn ret;
if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset,
ASF_GUID_OBJSIZE_SIZE, &guid_and_size)) != GST_FLOW_OK) {
GST_ERROR_OBJECT (asfparse, "Failed to pull data from headers");
goto leave;
}
asfparse->offset += ASF_GUID_OBJSIZE_SIZE;
size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (guid_and_size),
&(guids[ASF_HEADER_OBJECT_INDEX]));
if (size == 0) {
GST_ERROR_OBJECT (asfparse, "ASF starting identifier missing");
goto leave;
}
if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset,
size - ASF_GUID_OBJSIZE_SIZE, &headers)) != GST_FLOW_OK) {
GST_ERROR_OBJECT (asfparse, "Failed to pull data from headers");
goto leave;
}
headers = gst_buffer_join (guid_and_size, headers);
guid_and_size = NULL;
asfparse->offset += size - ASF_GUID_OBJSIZE_SIZE;
if (!gst_asf_parse_headers (headers, asfparse->asfinfo)) {
goto leave;
}
return gst_asf_parse_push (asfparse, headers);
leave:
if (headers)
gst_buffer_unref (headers);
if (guid_and_size)
gst_buffer_unref (guid_and_size);
return ret;
}
static GstFlowReturn
gst_asf_parse_pull_data_header (GstAsfParse * asfparse)
{
GstBuffer *buf = NULL;
GstFlowReturn ret;
if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset,
ASF_DATA_OBJECT_SIZE, &buf)) != GST_FLOW_OK) {
GST_ERROR_OBJECT (asfparse, "Failed to pull data header");
return ret;
}
asfparse->offset += ASF_DATA_OBJECT_SIZE;
asfparse->data_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buf),
&(guids[ASF_DATA_OBJECT_INDEX]));
if (asfparse->data_size == 0) {
GST_ERROR_OBJECT (asfparse, "Unexpected object, was expecting data object");
gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
return gst_asf_parse_parse_data_object (asfparse, buf);
}
static GstFlowReturn
gst_asf_parse_pull_packets (GstAsfParse * asfparse)
{
GstFlowReturn ret;
while (asfparse->asfinfo->broadcast ||
asfparse->parsed_packets < asfparse->asfinfo->packets_count) {
GstBuffer *packet = NULL;
GST_DEBUG_OBJECT (asfparse, "Parsing packet %" G_GUINT64_FORMAT,
asfparse->parsed_packets);
/* get the packet */
ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset,
asfparse->asfinfo->packet_size, &packet);
if (ret != GST_FLOW_OK)
return ret;
asfparse->parsed_packets++;
asfparse->offset += asfparse->asfinfo->packet_size;
/* parse the packet */
ret = gst_asf_parse_parse_packet (asfparse, packet);
if (ret != GST_FLOW_OK)
return ret;
}
return GST_FLOW_OK;
}
static GstFlowReturn
gst_asf_parse_pull_indexes (GstAsfParse * asfparse)
{
GstBuffer *guid_and_size = NULL;
GstBuffer *buf = NULL;
guint64 obj_size;
GstFlowReturn ret = GST_FLOW_OK;
while (1) {
ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset,
ASF_GUID_OBJSIZE_SIZE, &guid_and_size);
if (ret != GST_FLOW_OK)
break;
/* we can peek at the object size */
obj_size =
gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (guid_and_size), NULL);
if (obj_size == 0) {
GST_ERROR_OBJECT (asfparse, "Incomplete object found");
gst_buffer_unref (guid_and_size);
ret = GST_FLOW_ERROR;
break;
}
asfparse->offset += ASF_GUID_OBJSIZE_SIZE;
/* pull the rest of the object */
ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, obj_size,
&buf);
if (ret != GST_FLOW_OK) {
gst_buffer_unref (guid_and_size);
break;
}
asfparse->offset += obj_size - ASF_GUID_OBJSIZE_SIZE;
buf = gst_buffer_join (guid_and_size, buf);
ret = gst_asf_parse_push (asfparse, buf);
if (ret != GST_FLOW_OK)
break;
}
return ret;
}
static void
gst_asf_parse_loop (GstPad * pad)
{
GstFlowReturn ret = GST_FLOW_OK;
GstAsfParse *asfparse = GST_ASF_PARSE_CAST (GST_OBJECT_PARENT (pad));
GST_LOG_OBJECT (asfparse, "Processing data in loop function");
switch (asfparse->parse_state) {
case ASF_PARSING_HEADERS:
GST_INFO_OBJECT (asfparse, "Starting to parse headers");
ret = gst_asf_parse_pull_headers (asfparse);
if (ret != GST_FLOW_OK)
goto pause;
asfparse->parse_state = ASF_PARSING_DATA;
case ASF_PARSING_DATA:
GST_INFO_OBJECT (asfparse, "Parsing data object headers");
ret = gst_asf_parse_pull_data_header (asfparse);
if (ret != GST_FLOW_OK)
goto pause;
asfparse->parse_state = ASF_PARSING_PACKETS;
case ASF_PARSING_PACKETS:
GST_INFO_OBJECT (asfparse, "Starting packet parsing");
GST_INFO_OBJECT (asfparse, "Broadcast mode %s",
asfparse->asfinfo->broadcast ? "on" : "off");
ret = gst_asf_parse_pull_packets (asfparse);
if (ret != GST_FLOW_OK)
goto pause;
/* test if all packets have been processed */
if (!asfparse->asfinfo->broadcast &&
asfparse->parsed_packets == asfparse->asfinfo->packets_count) {
GST_INFO_OBJECT (asfparse,
"All %" G_GUINT64_FORMAT " packets processed",
asfparse->parsed_packets);
asfparse->parse_state = ASF_PARSING_INDEXES;
}
case ASF_PARSING_INDEXES:
/* we currently don't care about indexes, so just push them forward */
GST_INFO_OBJECT (asfparse, "Starting indexes parsing");
ret = gst_asf_parse_pull_indexes (asfparse);
if (ret != GST_FLOW_OK)
goto pause;
default:
break;
}
pause:
{
const gchar *reason = gst_flow_get_name (ret);
GST_INFO_OBJECT (asfparse, "Pausing sinkpad task");
gst_pad_pause_task (pad);
if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
if (ret == GST_FLOW_UNEXPECTED) {
} else {
GST_ELEMENT_ERROR (asfparse, STREAM, FAILED,
(NULL), ("streaming task paused, reason %s (%d)", reason, ret));
}
gst_pad_push_event (asfparse->srcpad, gst_event_new_eos ());
}
}
}
static GstFlowReturn
gst_asf_parse_chain (GstPad * pad, GstBuffer * buffer)
{
GstAsfParse *asfparse;
GstFlowReturn ret = GST_FLOW_OK;
asfparse = GST_ASF_PARSE (GST_PAD_PARENT (pad));
gst_adapter_push (asfparse->adapter, buffer);
switch (asfparse->parse_state) {
case ASF_PARSING_HEADERS:
if (asfparse->headers_size == 0 &&
gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) {
/* we can peek at the object size */
asfparse->headers_size =
gst_asf_match_and_peek_obj_size (gst_adapter_peek
(asfparse->adapter, ASF_GUID_OBJSIZE_SIZE),
&(guids[ASF_HEADER_OBJECT_INDEX]));
if (asfparse->headers_size == 0) {
/* something is wrong, this probably ain't an ASF stream */
GST_ERROR_OBJECT (asfparse, "ASF starting identifier missing");
ret = GST_FLOW_ERROR;
goto end;
}
}
if (gst_adapter_available (asfparse->adapter) >= asfparse->headers_size) {
GstBuffer *headers = gst_adapter_take_buffer (asfparse->adapter,
asfparse->headers_size);
if (gst_asf_parse_headers (headers, asfparse->asfinfo)) {
ret = gst_asf_parse_push (asfparse, headers);
asfparse->parse_state = ASF_PARSING_DATA;
} else {
ret = GST_FLOW_ERROR;
GST_ERROR_OBJECT (asfparse, "Failed to parse headers");
}
}
break;
case ASF_PARSING_DATA:
if (asfparse->data_size == 0 &&
gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) {
/* we can peek at the object size */
asfparse->data_size =
gst_asf_match_and_peek_obj_size (gst_adapter_peek
(asfparse->adapter, ASF_GUID_OBJSIZE_SIZE),
&(guids[ASF_DATA_OBJECT_INDEX]));
if (asfparse->data_size == 0) {
/* something is wrong */
GST_ERROR_OBJECT (asfparse, "Unexpected object after headers, was "
"expecting a data object");
ret = GST_FLOW_ERROR;
goto end;
}
}
/* if we have received the full data object headers */
if (gst_adapter_available (asfparse->adapter) >= ASF_DATA_OBJECT_SIZE) {
ret = gst_asf_parse_parse_data_object (asfparse,
gst_adapter_take_buffer (asfparse->adapter, ASF_DATA_OBJECT_SIZE));
if (ret != GST_FLOW_OK) {
goto end;
}
asfparse->parse_state = ASF_PARSING_PACKETS;
}
break;
case ASF_PARSING_PACKETS:
g_assert (asfparse->asfinfo->packet_size);
while ((asfparse->asfinfo->broadcast ||
asfparse->parsed_packets < asfparse->asfinfo->packets_count) &&
gst_adapter_available (asfparse->adapter) >=
asfparse->asfinfo->packet_size) {
GstBuffer *packet = gst_adapter_take_buffer (asfparse->adapter,
asfparse->asfinfo->packet_size);
asfparse->parsed_packets++;
ret = gst_asf_parse_parse_packet (asfparse, packet);
if (ret != GST_FLOW_OK)
goto end;
}
if (!asfparse->asfinfo->broadcast &&
asfparse->parsed_packets >= asfparse->asfinfo->packets_count) {
GST_INFO_OBJECT (asfparse, "Finished parsing packets");
asfparse->parse_state = ASF_PARSING_INDEXES;
}
break;
case ASF_PARSING_INDEXES:
/* we currently don't care about any of those objects */
if (gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) {
guint64 obj_size;
/* we can peek at the object size */
obj_size = gst_asf_match_and_peek_obj_size (gst_adapter_peek
(asfparse->adapter, ASF_GUID_OBJSIZE_SIZE), NULL);
if (gst_adapter_available (asfparse->adapter) >= obj_size) {
GST_DEBUG_OBJECT (asfparse, "Skiping object");
ret = gst_asf_parse_push (asfparse,
gst_adapter_take_buffer (asfparse->adapter, obj_size));
if (ret != GST_FLOW_OK) {
goto end;
}
}
}
break;
default:
break;
}
end:
return ret;
}
static void
gst_asf_parse_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_factory));
gst_element_class_set_details (element_class, &gst_asf_parse_details);
GST_DEBUG_CATEGORY_INIT (asfparse_debug, "asfparse", 0,
"Parser for ASF streams");
}
static void
gst_asf_parse_finalize (GObject * object)
{
GstAsfParse *asfparse = GST_ASF_PARSE (object);
gst_adapter_clear (asfparse->adapter);
g_object_unref (G_OBJECT (asfparse->adapter));
gst_caps_unref (asfparse->outcaps);
gst_asf_file_info_free (asfparse->asfinfo);
g_free (asfparse->packetinfo);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_asf_parse_class_init (GstAsfParseClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_class->get_property = gst_asf_parse_get_property;
gobject_class->set_property = gst_asf_parse_set_property;
gobject_class->finalize = gst_asf_parse_finalize;
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_asf_parse_change_state);
}
static void
gst_asf_parse_init (GstAsfParse * asfparse, GstAsfParseClass * klass)
{
asfparse->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
gst_pad_set_chain_function (asfparse->sinkpad, gst_asf_parse_chain);
gst_pad_set_activate_function (asfparse->sinkpad,
gst_asf_parse_sink_activate);
gst_pad_set_activatepull_function (asfparse->sinkpad,
gst_asf_parse_sink_activate_pull);
gst_element_add_pad (GST_ELEMENT (asfparse), asfparse->sinkpad);
asfparse->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
gst_pad_use_fixed_caps (asfparse->srcpad);
gst_element_add_pad (GST_ELEMENT (asfparse), asfparse->srcpad);
asfparse->adapter = gst_adapter_new ();
asfparse->outcaps = gst_caps_new_simple ("video/x-ms-asf", NULL);
asfparse->asfinfo = gst_asf_file_info_new ();
asfparse->packetinfo = g_new0 (GstAsfPacketInfo, 1);
gst_asf_parse_reset (asfparse);
}
static void
gst_asf_parse_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstAsfParse *asfparse;
asfparse = GST_ASF_PARSE (object);
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_asf_parse_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstAsfParse *asfparse;
asfparse = GST_ASF_PARSE (object);
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GstStateChangeReturn
gst_asf_parse_change_state (GstElement * element, GstStateChange transition)
{
GstAsfParse *asfparse;
GstStateChangeReturn ret;
asfparse = GST_ASF_PARSE (element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_asf_parse_reset (asfparse);
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
goto done;
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
default:
break;
}
done:
return ret;
}
gboolean
gst_asf_parse_plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "asfparse",
GST_RANK_NONE, GST_TYPE_ASF_PARSE);
}

88
gst/asfmux/gstasfparse.h Normal file
View file

@ -0,0 +1,88 @@
/* ASF Parser plugin for GStreamer
* Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* 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
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_ASF_PARSE_H__
#define __GST_ASF_PARSE_H__
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
#include <gst/base/gstbytereader.h>
#include "gstasfobjects.h"
G_BEGIN_DECLS
#define GST_TYPE_ASF_PARSE \
(gst_asf_parse_get_type())
#define GST_ASF_PARSE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASF_PARSE,GstAsfParse))
#define GST_ASF_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASF_PARSE,GstAsfParseClass))
#define GST_IS_ASF_PARSE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASF_PARSE))
#define GST_IS_ASF_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASF_PARSE))
#define GST_ASF_PARSE_CAST(obj) ((GstAsfParse*)(obj))
enum GstAsfParsingState {
ASF_PARSING_HEADERS,
ASF_PARSING_DATA,
ASF_PARSING_PACKETS,
ASF_PARSING_INDEXES
};
typedef struct _GstAsfParse GstAsfParse;
typedef struct _GstAsfParseClass GstAsfParseClass;
struct _GstAsfParse {
GstElement element;
enum GstAsfParsingState parse_state;
GstAdapter *adapter;
GstPad *srcpad;
GstPad *sinkpad;
GstCaps *outcaps;
guint64 parsed_packets;
guint64 offset; /* used in pull mode */
/* parsed info */
GstAsfFileInfo *asfinfo;
GstAsfPacketInfo *packetinfo; /* we keep it here to avoid allocs */
guint64 headers_size;
guint64 data_size;
};
struct _GstAsfParseClass {
GstElementClass parent_class;
};
GType gst_asf_parse_get_type(void);
gboolean gst_asf_parse_plugin_init (GstPlugin * plugin);
G_END_DECLS
#endif /* __GST_ASF_PARSE_H__ */

446
gst/asfmux/gstrtpasfpay.c Normal file
View file

@ -0,0 +1,446 @@
/* ASF RTP Payloader plugin for GStreamer
* Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* 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
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* FIXME
* - this element doesn't follow (max/min) time properties,
* is it possible to do it with a container format?
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gst/rtp/gstrtpbuffer.h>
#include <string.h>
#include "gstrtpasfpay.h"
GST_DEBUG_CATEGORY_STATIC (rtpasfpay_debug);
#define GST_CAT_DEFAULT (rtpasfpay_debug)
/* elementfactory information */
static const GstElementDetails gst_rtp_asf_pay_details =
GST_ELEMENT_DETAILS ("RTP ASF payloader",
"Codec/Payloader/Network",
"Payload-encodes ASF into RTP packets (MS_RTSP)",
"Thiago Santos <thiagoss@embedded.ufcg.edu.br>");
static GstStaticPadTemplate gst_rtp_asf_pay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-ms-asf, " "parsed = (boolean) true")
);
static GstStaticPadTemplate gst_rtp_asf_pay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp, "
"media = (string) {\"audio\", \"video\", \"application\"}, "
"clock-rate = (int) 1000, " "encoding-name = (string) \"X-ASF-PF\"")
);
static GstFlowReturn
gst_rtp_asf_pay_handle_buffer (GstBaseRTPPayload * rtppay, GstBuffer * buffer);
static gboolean
gst_rtp_asf_pay_set_caps (GstBaseRTPPayload * rtppay, GstCaps * caps);
GST_BOILERPLATE (GstRtpAsfPay, gst_rtp_asf_pay, GstBaseRTPPayload,
GST_TYPE_BASE_RTP_PAYLOAD);
static void
gst_rtp_asf_pay_init (GstRtpAsfPay * rtpasfpay, GstRtpAsfPayClass * klass)
{
rtpasfpay->first_ts = 0;
rtpasfpay->config = NULL;
rtpasfpay->packets_count = 0;
rtpasfpay->state = ASF_NOT_STARTED;
rtpasfpay->headers = NULL;
rtpasfpay->current = NULL;
}
static void
gst_rtp_asf_pay_finalize (GObject * object)
{
GstRtpAsfPay *rtpasfpay;
rtpasfpay = GST_RTP_ASF_PAY (object);
g_free (rtpasfpay->config);
if (rtpasfpay->headers)
gst_buffer_unref (rtpasfpay->headers);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_rtp_asf_pay_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_asf_pay_sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_asf_pay_src_template));
gst_element_class_set_details (element_class, &gst_rtp_asf_pay_details);
}
static void
gst_rtp_asf_pay_class_init (GstRtpAsfPayClass * klass)
{
GObjectClass *gobject_class;
GstBaseRTPPayloadClass *gstbasertppayload_class;
gobject_class = (GObjectClass *) klass;
gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
gobject_class->finalize = gst_rtp_asf_pay_finalize;
gstbasertppayload_class->handle_buffer = gst_rtp_asf_pay_handle_buffer;
gstbasertppayload_class->set_caps = gst_rtp_asf_pay_set_caps;
GST_DEBUG_CATEGORY_INIT (rtpasfpay_debug, "rtpasfpay", 0,
"ASF RTP Payloader");
}
static gboolean
gst_rtp_asf_pay_set_caps (GstBaseRTPPayload * rtppay, GstCaps * caps)
{
/* FIXME change application for the actual content */
gst_basertppayload_set_options (rtppay, "application", TRUE, "X-ASF-PF",
1000);
return TRUE;
}
static GstFlowReturn
gst_rtp_asf_pay_handle_packet (GstRtpAsfPay * rtpasfpay, GstBuffer * buffer)
{
GstBaseRTPPayload *rtppay;
GstAsfPacketInfo *packetinfo;
guint8 flags;
guint8 *data;
guint32 packet_util_size;
guint32 packet_offset;
guint32 size_left;
GstFlowReturn ret = GST_FLOW_OK;
rtppay = GST_BASE_RTP_PAYLOAD (rtpasfpay);
packetinfo = &rtpasfpay->packetinfo;
if (!gst_asf_parse_packet (buffer, packetinfo, TRUE)) {
GST_ERROR_OBJECT (rtpasfpay, "Error while parsing asf packet");
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
if (packetinfo->packet_size == 0)
packetinfo->packet_size = rtpasfpay->asfinfo.packet_size;
GST_LOG_OBJECT (rtpasfpay, "Packet size: %" G_GUINT32_FORMAT
", padding: %" G_GUINT32_FORMAT, packetinfo->packet_size,
packetinfo->padding);
/* FIXME - should update the padding field to 0 */
packet_util_size = packetinfo->packet_size - packetinfo->padding;
packet_offset = 0;
while (packet_util_size > 0) {
/* Even if we don't fill completely an output buffer we
* push it when we add an fragment. Because it seems that
* it is not possible to determine where a asf packet
* fragment ends inside a rtp packet payload.
* This flag tells us to push the packet.
*/
gboolean force_push = FALSE;
/* we have no output buffer pending, create one */
if (rtpasfpay->current == NULL) {
GST_LOG_OBJECT (rtpasfpay, "Creating new output buffer");
rtpasfpay->current =
gst_rtp_buffer_new_allocate_len (GST_BASE_RTP_PAYLOAD_MTU (rtpasfpay),
0, 0);
rtpasfpay->cur_off = gst_rtp_buffer_get_header_len (rtpasfpay->current);
rtpasfpay->has_ts = FALSE;
rtpasfpay->marker = FALSE;
}
data = GST_BUFFER_DATA (rtpasfpay->current) + rtpasfpay->cur_off;
size_left = GST_BUFFER_SIZE (rtpasfpay->current) - rtpasfpay->cur_off;
GST_DEBUG_OBJECT (rtpasfpay, "Input buffer bytes consumed: %"
G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT, packet_offset,
GST_BUFFER_SIZE (buffer));
GST_DEBUG_OBJECT (rtpasfpay, "Output rtpbuffer status");
GST_DEBUG_OBJECT (rtpasfpay, "Current offset: %" G_GUINT32_FORMAT,
rtpasfpay->cur_off);
GST_DEBUG_OBJECT (rtpasfpay, "Size left: %" G_GUINT32_FORMAT, size_left);
GST_DEBUG_OBJECT (rtpasfpay, "Has ts: %s",
rtpasfpay->has_ts ? "yes" : "no");
if (rtpasfpay->has_ts) {
GST_DEBUG_OBJECT (rtpasfpay, "Ts: %" G_GUINT32_FORMAT, rtpasfpay->ts);
}
flags = 0;
if (packetinfo->has_keyframe) {
flags = flags | 0x80;
}
flags = flags | 0x20; /* Relative timestamp is present */
if (!rtpasfpay->has_ts) {
/* this is the first asf packet, its send time is the
* rtp packet timestamp */
rtpasfpay->has_ts = TRUE;
rtpasfpay->ts = packetinfo->send_time;
}
if (GST_BUFFER_SIZE (rtpasfpay->current) - rtpasfpay->cur_off >=
packet_util_size + 8) {
/* enough space for the rest of the packet */
if (packet_offset == 0) {
flags = flags | 0x40;
GST_WRITE_UINT24_BE (data + 1, packet_util_size);
} else {
GST_WRITE_UINT24_BE (data + 1, packet_offset);
force_push = TRUE;
}
data[0] = flags;
GST_WRITE_UINT32_BE (data + 4,
(gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts);
memcpy (data + 8, GST_BUFFER_DATA (buffer) + packet_offset,
packet_util_size);
/* updating status variables */
rtpasfpay->cur_off += 8 + packet_util_size;
size_left -= packet_util_size + 8;
packet_offset += packet_util_size;
packet_util_size = 0;
rtpasfpay->marker = TRUE;
} else {
/* fragment packet */
data[0] = flags;
GST_WRITE_UINT24_BE (data + 1, packet_offset);
GST_WRITE_UINT32_BE (data + 4,
(gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts);
memcpy (data + 8, GST_BUFFER_DATA (buffer) + packet_offset,
size_left - 8);
/* updating status variables */
rtpasfpay->cur_off += size_left;
packet_offset += size_left - 8;
packet_util_size -= size_left - 8;
size_left = 0;
force_push = TRUE;
}
/* there is not enough room for any more buffers */
if (force_push || size_left <= 8) {
if (size_left != 0) {
/* trim remaining bytes not used */
GstBuffer *aux = gst_buffer_create_sub (rtpasfpay->current, 0,
GST_BUFFER_SIZE (rtpasfpay->current) - size_left);
gst_buffer_unref (rtpasfpay->current);
rtpasfpay->current = aux;
}
gst_rtp_buffer_set_ssrc (rtpasfpay->current, rtppay->current_ssrc);
gst_rtp_buffer_set_marker (rtpasfpay->current, rtpasfpay->marker);
gst_rtp_buffer_set_payload_type (rtpasfpay->current,
GST_BASE_RTP_PAYLOAD_PT (rtppay));
gst_rtp_buffer_set_seq (rtpasfpay->current, rtppay->seqnum + 1);
gst_rtp_buffer_set_timestamp (rtpasfpay->current, packetinfo->send_time);
GST_BUFFER_TIMESTAMP (rtpasfpay->current) = GST_BUFFER_TIMESTAMP (buffer);
gst_buffer_set_caps (rtpasfpay->current,
GST_PAD_CAPS (GST_BASE_RTP_PAYLOAD_SRCPAD (rtppay)));
rtppay->seqnum++;
rtppay->timestamp = packetinfo->send_time;
GST_DEBUG_OBJECT (rtpasfpay, "Pushing rtp buffer");
ret =
gst_pad_push (GST_BASE_RTP_PAYLOAD_SRCPAD (rtppay),
rtpasfpay->current);
rtpasfpay->current = NULL;
if (ret != GST_FLOW_OK) {
gst_buffer_unref (buffer);
return ret;
}
}
}
gst_buffer_unref (buffer);
return ret;
}
static GstFlowReturn
gst_rtp_asf_pay_parse_headers (GstRtpAsfPay * rtpasfpay)
{
GstFlowReturn ret = GST_FLOW_OK;
gchar *maxps;
g_return_val_if_fail (rtpasfpay->headers, GST_FLOW_ERROR);
if (!gst_asf_parse_headers (rtpasfpay->headers, &rtpasfpay->asfinfo))
goto error;
GST_DEBUG_OBJECT (rtpasfpay, "Packets number: %" G_GUINT64_FORMAT,
rtpasfpay->asfinfo.packets_count);
GST_DEBUG_OBJECT (rtpasfpay, "Packets size: %" G_GUINT32_FORMAT,
rtpasfpay->asfinfo.packet_size);
GST_DEBUG_OBJECT (rtpasfpay, "Broadcast mode: %s",
rtpasfpay->asfinfo.broadcast ? "true" : "false");
/* get the config for caps */
g_free (rtpasfpay->config);
rtpasfpay->config = g_base64_encode (GST_BUFFER_DATA (rtpasfpay->headers),
GST_BUFFER_SIZE (rtpasfpay->headers));
GST_DEBUG_OBJECT (rtpasfpay, "Serialized headers to base64 string %s",
rtpasfpay->config);
g_assert (rtpasfpay->config != NULL);
GST_DEBUG_OBJECT (rtpasfpay, "Setting optional caps values: maxps=%"
G_GUINT32_FORMAT " and config=%s", rtpasfpay->asfinfo.packet_size,
rtpasfpay->config);
maxps =
g_strdup_printf ("%" G_GUINT32_FORMAT, rtpasfpay->asfinfo.packet_size);
gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpasfpay), "maxps",
G_TYPE_STRING, maxps, "config", G_TYPE_STRING, rtpasfpay->config, NULL);
g_free (maxps);
return GST_FLOW_OK;
error:
ret = GST_FLOW_ERROR;
GST_ERROR_OBJECT (rtpasfpay, "Error while parsing headers");
return GST_FLOW_ERROR;
}
static GstFlowReturn
gst_rtp_asf_pay_handle_buffer (GstBaseRTPPayload * rtppay, GstBuffer * buffer)
{
GstRtpAsfPay *rtpasfpay = GST_RTP_ASF_PAY_CAST (rtppay);
if (G_UNLIKELY (rtpasfpay->state == ASF_END)) {
GST_LOG_OBJECT (rtpasfpay,
"Dropping buffer as we already pushed all packets");
gst_buffer_unref (buffer);
return GST_FLOW_UNEXPECTED; /* we already finished our job */
}
/* receive headers
* we only accept if they are in a single buffer */
if (G_UNLIKELY (rtpasfpay->state == ASF_NOT_STARTED)) {
guint64 header_size;
if (GST_BUFFER_SIZE (buffer) < 24) { /* guid+object size size */
GST_ERROR_OBJECT (rtpasfpay,
"Buffer too small, smaller than a Guid and object size");
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
header_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buffer),
&(guids[ASF_HEADER_OBJECT_INDEX]));
if (header_size > 0) {
GST_DEBUG_OBJECT (rtpasfpay, "ASF header guid received, size %"
G_GUINT64_FORMAT, header_size);
if (GST_BUFFER_SIZE (buffer) < header_size) {
GST_ERROR_OBJECT (rtpasfpay, "Headers should be contained in a single"
" buffer");
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
} else {
rtpasfpay->state = ASF_DATA_OBJECT;
/* clear previous headers, if any */
if (rtpasfpay->headers) {
gst_buffer_unref (rtpasfpay->headers);
}
GST_DEBUG_OBJECT (rtpasfpay, "Storing headers");
if (GST_BUFFER_SIZE (buffer) == header_size) {
rtpasfpay->headers = buffer;
return GST_FLOW_OK;
} else {
/* headers are a subbuffer of thie buffer */
GstBuffer *aux = gst_buffer_create_sub (buffer, header_size,
GST_BUFFER_SIZE (buffer) - header_size);
rtpasfpay->headers = gst_buffer_create_sub (buffer, 0, header_size);
gst_buffer_replace (&buffer, aux);
}
}
} else {
GST_ERROR_OBJECT (rtpasfpay, "Missing ASF header start");
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
}
if (G_UNLIKELY (rtpasfpay->state == ASF_DATA_OBJECT)) {
if (GST_BUFFER_SIZE (buffer) != ASF_DATA_OBJECT_SIZE) {
GST_ERROR_OBJECT (rtpasfpay, "Received buffer of different size of "
"the data object header");
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
if (gst_asf_match_guid (GST_BUFFER_DATA (buffer),
&(guids[ASF_DATA_OBJECT_INDEX]))) {
GST_DEBUG_OBJECT (rtpasfpay, "Received data object header");
rtpasfpay->headers = gst_buffer_join (rtpasfpay->headers, buffer);
rtpasfpay->state = ASF_PACKETS;
return gst_rtp_asf_pay_parse_headers (rtpasfpay);
} else {
GST_ERROR_OBJECT (rtpasfpay, "Unexpected object received (was expecting "
"data object)");
gst_buffer_unref (buffer);
return GST_FLOW_ERROR;
}
}
if (G_LIKELY (rtpasfpay->state == ASF_PACKETS)) {
/* in broadcast mode we can't trust the packets count information
* from the headers
* We assume that if this is on broadcast mode it is a live stream
* and we are going to keep receiving packets indefinitely
*/
if (rtpasfpay->asfinfo.broadcast ||
rtpasfpay->packets_count < rtpasfpay->asfinfo.packets_count) {
GST_DEBUG_OBJECT (rtpasfpay, "Received packet %"
G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
rtpasfpay->packets_count, rtpasfpay->asfinfo.packets_count);
rtpasfpay->packets_count++;
return gst_rtp_asf_pay_handle_packet (rtpasfpay, buffer);
} else {
GST_INFO_OBJECT (rtpasfpay, "Packets ended");
rtpasfpay->state = ASF_END;
gst_buffer_unref (buffer);
return GST_FLOW_UNEXPECTED;
}
}
gst_buffer_unref (buffer);
return GST_FLOW_OK;
}
gboolean
gst_rtp_asf_pay_plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "rtpasfpay",
GST_RANK_NONE, GST_TYPE_RTP_ASF_PAY);
}

87
gst/asfmux/gstrtpasfpay.h Normal file
View file

@ -0,0 +1,87 @@
/* ASF RTP Payloader plugin for GStreamer
* Copyright (C) 2009 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
*
* 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
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_RTP_ASF_PAY_H__
#define __GST_RTP_ASF_PAY_H__
#include <gst/gst.h>
#include <gst/rtp/gstbasertppayload.h>
#include <gst/rtp/gstrtpbuffer.h>
#include <gst/base/gstadapter.h>
#include "gstasfobjects.h"
G_BEGIN_DECLS
#define GST_TYPE_RTP_ASF_PAY \
(gst_rtp_asf_pay_get_type())
#define GST_RTP_ASF_PAY(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_ASF_PAY,GstRtpAsfPay))
#define GST_RTP_ASF_PAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_ASF_PAY,GstRtpAsfPayClass))
#define GST_IS_RTP_ASF_PAY(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_ASF_PAY))
#define GST_IS_RTP_ASF_PAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_ASF_PAY))
#define GST_RTP_ASF_PAY_CAST(obj) ((GstRtpAsfPay*)(obj))
enum GstRtpAsfPayState
{
ASF_NOT_STARTED,
ASF_DATA_OBJECT,
ASF_PACKETS,
ASF_END
};
typedef struct _GstRtpAsfPay GstRtpAsfPay;
typedef struct _GstRtpAsfPayClass GstRtpAsfPayClass;
struct _GstRtpAsfPay
{
GstBaseRTPPayload rtppay;
enum GstRtpAsfPayState state;
guint32 first_ts;
gchar *config;
guint64 packets_count;
GstAsfFileInfo asfinfo;
/* current output buffer */
GstBuffer *current;
guint32 cur_off;
guint32 ts;
gboolean has_ts;
gboolean marker;
/* keeping it here to avoid allocs/frees */
GstAsfPacketInfo packetinfo;
GstBuffer *headers;
};
struct _GstRtpAsfPayClass
{
GstBaseRTPPayloadClass parent_class;
};
GType gst_rtp_asf_pay_get_type (void);
gboolean gst_rtp_asf_pay_plugin_init (GstPlugin * plugin);
G_END_DECLS
#endif /* __GST_RTP_ASF_PAY_H__ */