mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-13 11:34:10 +00:00
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:
parent
30feab574a
commit
2641cd9d94
11 changed files with 4732 additions and 0 deletions
|
@ -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
24
gst/asfmux/Makefile.am
Normal 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
51
gst/asfmux/gstasf.c
Normal 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
2259
gst/asfmux/gstasfmux.c
Normal file
File diff suppressed because it is too large
Load diff
159
gst/asfmux/gstasfmux.h
Normal file
159
gst/asfmux/gstasfmux.h
Normal 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
803
gst/asfmux/gstasfobjects.c
Normal 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
190
gst/asfmux/gstasfobjects.h
Normal 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
623
gst/asfmux/gstasfparse.c
Normal 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
88
gst/asfmux/gstasfparse.h
Normal 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
446
gst/asfmux/gstrtpasfpay.c
Normal 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
87
gst/asfmux/gstrtpasfpay.h
Normal 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__ */
|
Loading…
Reference in a new issue