From aaf8407f8584de6a6ffa1c1dec85099bca240a83 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 29 Jan 2002 20:06:07 +0000 Subject: [PATCH] inclusion of GstMD5Sink Original commit message from CVS: inclusion of GstMD5Sink --- gst/elements/Makefile.am | 6 +- gst/elements/gstelements.c | 2 + gst/elements/gstmd5sink.c | 512 +++++++++++++++++++++++++++++++++ gst/elements/gstmd5sink.h | 86 ++++++ plugins/elements/Makefile.am | 6 +- plugins/elements/gstelements.c | 2 + plugins/elements/gstmd5sink.c | 512 +++++++++++++++++++++++++++++++++ plugins/elements/gstmd5sink.h | 86 ++++++ 8 files changed, 1208 insertions(+), 4 deletions(-) create mode 100644 gst/elements/gstmd5sink.c create mode 100644 gst/elements/gstmd5sink.h create mode 100644 plugins/elements/gstmd5sink.c create mode 100644 plugins/elements/gstmd5sink.h diff --git a/gst/elements/Makefile.am b/gst/elements/Makefile.am index 6ea10ff4d3..a274fb9217 100644 --- a/gst/elements/Makefile.am +++ b/gst/elements/Makefile.am @@ -16,7 +16,8 @@ libgstelements_la_SOURCES = \ gstpipefilter.c \ gsttee.c \ gstaggregator.c \ - gststatistics.c + gststatistics.c \ + gstmd5sink.c libgstelements_la_CFLAGS = $(GST_CFLAGS) libgstelements_la_LIBADD = $(GST_LIBS) libgstelements_la_LDFLAGS = -version-info $(GST_LIBVERSION) @@ -33,4 +34,5 @@ noinst_HEADERS = \ gsttee.h \ gstaggregator.h \ gststatistics.h \ - gstfilesrc.h + gstfilesrc.h \ + gstmd5sink.h diff --git a/gst/elements/gstelements.c b/gst/elements/gstelements.c index 589da3965b..fe48108cc5 100644 --- a/gst/elements/gstelements.c +++ b/gst/elements/gstelements.c @@ -34,6 +34,7 @@ #include "gsttee.h" #include "gstaggregator.h" #include "gststatistics.h" +#include "gstmd5sink.h" struct _elements_entry { @@ -60,6 +61,7 @@ static struct _elements_entry _elements[] = { { "tee", gst_tee_get_type, &gst_tee_details, gst_tee_factory_init }, { "aggregator", gst_aggregator_get_type, &gst_aggregator_details, gst_aggregator_factory_init }, { "statistics", gst_statistics_get_type, &gst_statistics_details, NULL }, + { "md5sink", gst_md5sink_get_type, &gst_md5sink_details, gst_md5sink_factory_init }, { NULL, 0 }, }; diff --git a/gst/elements/gstmd5sink.c b/gst/elements/gstmd5sink.c new file mode 100644 index 0000000000..335d662825 --- /dev/null +++ b/gst/elements/gstmd5sink.c @@ -0,0 +1,512 @@ +/* GStreamer + * Copyright (C) 2002 Erik Walthinsen + * 2002 Wim Taymans + * + * gstmd5sink.c: A sink computing an md5 checksum from a stream + * + * The md5 code was taken from glibc-2.2.3/crypt/md5.c and slightly + * modified. + * + * 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 +#include + +# define SWAP(n) (n) +#ifdef _LIBC +# include +# if __BYTE_ORDER == __BIG_ENDIAN +# undef SWAP +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +# endif +#endif + +#include +#include + +#include "gstmd5sink.h" + +/* MD5Sink signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_MD5, + /* FILL ME */ +}; + +GST_PADTEMPLATE_FACTORY (md5_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + NULL /* no caps */ +); + +/* GObject stuff */ +static void gst_md5sink_class_init (GstMD5SinkClass *klass); +static void gst_md5sink_init (GstMD5Sink *md5sink); + +/*static void gst_md5sink_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec);*/ +static void gst_md5sink_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); + +static void gst_md5sink_chain (GstPad *pad, GstBuffer *buf); +static GstElementStateReturn gst_md5sink_change_state (GstElement *element); + +/* variables */ +static GstElementClass *parent_class = NULL; +/* no signals +static guint gst_md5sink_signals[LAST_SIGNAL] = { 0 }; */ + + +/* MD5 stuff */ +void md5_init_ctx (GstMD5Sink *ctx); +gpointer md5_read_ctx (GstMD5Sink *ctx, gpointer resbuf); +gpointer md5_finish_ctx (GstMD5Sink *ctx, gpointer resbuf); +void md5_process_bytes (const void *buffer, size_t len, GstMD5Sink *ctx); +void md5_process_block (const void *buffer, size_t len, GstMD5Sink *ctx); + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const guchar fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + +/* MD5 functions */ +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (GstMD5Sink *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +gpointer +md5_finish_ctx (GstMD5Sink *ctx, gpointer resbuf) +{ + /* Take yet unprocessed bytes into account. */ + guint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(guint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(guint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +gpointer +md5_read_ctx (GstMD5Sink *ctx, gpointer resbuf) +{ + ((guint32 *) resbuf)[0] = SWAP (ctx->A); + ((guint32 *) resbuf)[1] = SWAP (ctx->B); + ((guint32 *) resbuf)[2] = SWAP (ctx->C); + ((guint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +void +md5_process_bytes (const void *buffer, size_t len, GstMD5Sink *ctx) +{ + //const void aligned_buffer = buffer; + + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + /* Only put full words in the buffer. */ + add -= add % __alignof__ (guint32); + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len > 64) + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + md5_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ +void +md5_process_block (const void *buffer, size_t len, GstMD5Sink *ctx) +{ + guint32 correct_words[16]; + const guint32 *words = buffer; + size_t nwords = len / sizeof (guint32); + const guint32 *endp = words + nwords; + guint32 A = ctx->A; + guint32 B = ctx->B; + guint32 C = ctx->C; + guint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + guint32 *cwp = correct_words; + guint32 A_save = A; + guint32 B_save = B; + guint32 C_save = C; + guint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} + +GType +gst_md5sink_get_type (void) +{ + static GType md5sink_type = 0; + + if (!md5sink_type) { + static const GTypeInfo md5sink_info = { + sizeof(GstMD5SinkClass), + NULL, + NULL, + (GClassInitFunc) gst_md5sink_class_init, + NULL, + NULL, + sizeof(GstMD5Sink), + 0, + (GInstanceInitFunc) gst_md5sink_init, + }; + md5sink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstMD5Sink", &md5sink_info, 0); + } + return md5sink_type; +} + +static void +gst_md5sink_class_init (GstMD5SinkClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MD5, + g_param_spec_pointer ("md5", "md5", "current value of the md5 sum", + G_PARAM_READABLE)); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_md5sink_change_state); + + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_md5sink_get_property); +} + +static void +gst_md5sink_init (GstMD5Sink *md5sink) +{ + GstPad *pad; + pad = gst_pad_new_from_template (GST_PADTEMPLATE_GET (md5_sink_factory), "sink"); + gst_element_add_pad (GST_ELEMENT (md5sink), pad); + gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_md5sink_chain)); + + md5_init_ctx (md5sink); +} + +static GstElementStateReturn +gst_md5sink_change_state (GstElement *element) +{ + GstMD5Sink *sink; + + /* element check */ + sink = GST_MD5SINK (element); + g_return_val_if_fail (sink != NULL, GST_PAD_CONNECT_REFUSED); + g_return_val_if_fail (GST_IS_MD5SINK (sink), GST_PAD_CONNECT_REFUSED); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_READY_TO_PAUSED: + md5_init_ctx (sink); + break; + case GST_STATE_PAUSED_TO_READY: + md5_finish_ctx (sink, sink->md5); + break; + default: + break; + } + + if ((GST_ELEMENT_CLASS (parent_class)->change_state)) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; +} + +static void +gst_md5sink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstMD5Sink *sink; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_MD5SINK (object)); + + sink = GST_MD5SINK (object); + + switch (prop_id) { + case ARG_MD5: + md5_read_ctx (sink, sink->md5); + g_value_set_pointer (value, sink->md5); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_md5sink_chain (GstPad *pad, GstBuffer *buf) +{ + GstMD5Sink *md5sink; + + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (buf != NULL); + + md5sink = GST_MD5SINK (gst_pad_get_parent (pad)); + + if (GST_IS_BUFFER (buf)) + { + md5_process_bytes (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), md5sink); + } + + gst_buffer_unref (buf); +} + +GstElementDetails gst_md5sink_details = { + "MD5 Sink", + "Sink", + "compute MD5 for incoming data", + VERSION, + "Benjamin Otte ", + "(C) 2002", +}; + +gboolean +gst_md5sink_factory_init (GstElementFactory *factory) +{ + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (md5_sink_factory)); + + return TRUE; +} + diff --git a/gst/elements/gstmd5sink.h b/gst/elements/gstmd5sink.h new file mode 100644 index 0000000000..7d3377ce5f --- /dev/null +++ b/gst/elements/gstmd5sink.h @@ -0,0 +1,86 @@ +/* GStreamer + * Copyright (C) 2002 Erik Walthinsen + * 2002 Wim Taymans + * + * gstmd5sink.h: + * + * 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_MD5SINK_H__ +#define __GST_MD5SINK_H__ + + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +GstElementDetails gst_md5sink_details; + + +#define GST_TYPE_MD5SINK \ + (gst_md5sink_get_type()) +#define GST_MD5SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MD5SINK,GstMD5Sink)) +#define GST_MD5SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MD5SINK,GstMD5SinkClass)) +#define GST_IS_MD5SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MD5SINK)) +#define GST_IS_MD5SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MD5SINK)) + +typedef struct _GstMD5Sink GstMD5Sink; +typedef struct _GstMD5SinkClass GstMD5SinkClass; + +struct _GstMD5Sink { + GstElement element; + + /* md5 information */ + guint32 A; + guint32 B; + guint32 C; + guint32 D; + + guint32 total[2]; + guint32 buflen; + gchar buffer[128] __attribute__ ((__aligned__ (__alignof__ (guint32)))); + + /* latest md5 */ + guchar md5[16]; + +}; + +struct _GstMD5SinkClass { + GstElementClass parent_class; + +}; + +GType gst_md5sink_get_type (void); + +gboolean gst_md5sink_factory_init (GstElementFactory *factory); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_MD5SINK_H__ */ diff --git a/plugins/elements/Makefile.am b/plugins/elements/Makefile.am index 6ea10ff4d3..a274fb9217 100644 --- a/plugins/elements/Makefile.am +++ b/plugins/elements/Makefile.am @@ -16,7 +16,8 @@ libgstelements_la_SOURCES = \ gstpipefilter.c \ gsttee.c \ gstaggregator.c \ - gststatistics.c + gststatistics.c \ + gstmd5sink.c libgstelements_la_CFLAGS = $(GST_CFLAGS) libgstelements_la_LIBADD = $(GST_LIBS) libgstelements_la_LDFLAGS = -version-info $(GST_LIBVERSION) @@ -33,4 +34,5 @@ noinst_HEADERS = \ gsttee.h \ gstaggregator.h \ gststatistics.h \ - gstfilesrc.h + gstfilesrc.h \ + gstmd5sink.h diff --git a/plugins/elements/gstelements.c b/plugins/elements/gstelements.c index 589da3965b..fe48108cc5 100644 --- a/plugins/elements/gstelements.c +++ b/plugins/elements/gstelements.c @@ -34,6 +34,7 @@ #include "gsttee.h" #include "gstaggregator.h" #include "gststatistics.h" +#include "gstmd5sink.h" struct _elements_entry { @@ -60,6 +61,7 @@ static struct _elements_entry _elements[] = { { "tee", gst_tee_get_type, &gst_tee_details, gst_tee_factory_init }, { "aggregator", gst_aggregator_get_type, &gst_aggregator_details, gst_aggregator_factory_init }, { "statistics", gst_statistics_get_type, &gst_statistics_details, NULL }, + { "md5sink", gst_md5sink_get_type, &gst_md5sink_details, gst_md5sink_factory_init }, { NULL, 0 }, }; diff --git a/plugins/elements/gstmd5sink.c b/plugins/elements/gstmd5sink.c new file mode 100644 index 0000000000..335d662825 --- /dev/null +++ b/plugins/elements/gstmd5sink.c @@ -0,0 +1,512 @@ +/* GStreamer + * Copyright (C) 2002 Erik Walthinsen + * 2002 Wim Taymans + * + * gstmd5sink.c: A sink computing an md5 checksum from a stream + * + * The md5 code was taken from glibc-2.2.3/crypt/md5.c and slightly + * modified. + * + * 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 +#include + +# define SWAP(n) (n) +#ifdef _LIBC +# include +# if __BYTE_ORDER == __BIG_ENDIAN +# undef SWAP +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +# endif +#endif + +#include +#include + +#include "gstmd5sink.h" + +/* MD5Sink signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_MD5, + /* FILL ME */ +}; + +GST_PADTEMPLATE_FACTORY (md5_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + NULL /* no caps */ +); + +/* GObject stuff */ +static void gst_md5sink_class_init (GstMD5SinkClass *klass); +static void gst_md5sink_init (GstMD5Sink *md5sink); + +/*static void gst_md5sink_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec);*/ +static void gst_md5sink_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); + +static void gst_md5sink_chain (GstPad *pad, GstBuffer *buf); +static GstElementStateReturn gst_md5sink_change_state (GstElement *element); + +/* variables */ +static GstElementClass *parent_class = NULL; +/* no signals +static guint gst_md5sink_signals[LAST_SIGNAL] = { 0 }; */ + + +/* MD5 stuff */ +void md5_init_ctx (GstMD5Sink *ctx); +gpointer md5_read_ctx (GstMD5Sink *ctx, gpointer resbuf); +gpointer md5_finish_ctx (GstMD5Sink *ctx, gpointer resbuf); +void md5_process_bytes (const void *buffer, size_t len, GstMD5Sink *ctx); +void md5_process_block (const void *buffer, size_t len, GstMD5Sink *ctx); + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const guchar fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + +/* MD5 functions */ +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (GstMD5Sink *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +gpointer +md5_finish_ctx (GstMD5Sink *ctx, gpointer resbuf) +{ + /* Take yet unprocessed bytes into account. */ + guint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(guint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(guint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +gpointer +md5_read_ctx (GstMD5Sink *ctx, gpointer resbuf) +{ + ((guint32 *) resbuf)[0] = SWAP (ctx->A); + ((guint32 *) resbuf)[1] = SWAP (ctx->B); + ((guint32 *) resbuf)[2] = SWAP (ctx->C); + ((guint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +void +md5_process_bytes (const void *buffer, size_t len, GstMD5Sink *ctx) +{ + //const void aligned_buffer = buffer; + + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + /* Only put full words in the buffer. */ + add -= add % __alignof__ (guint32); + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len > 64) + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + md5_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ +void +md5_process_block (const void *buffer, size_t len, GstMD5Sink *ctx) +{ + guint32 correct_words[16]; + const guint32 *words = buffer; + size_t nwords = len / sizeof (guint32); + const guint32 *endp = words + nwords; + guint32 A = ctx->A; + guint32 B = ctx->B; + guint32 C = ctx->C; + guint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + guint32 *cwp = correct_words; + guint32 A_save = A; + guint32 B_save = B; + guint32 C_save = C; + guint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} + +GType +gst_md5sink_get_type (void) +{ + static GType md5sink_type = 0; + + if (!md5sink_type) { + static const GTypeInfo md5sink_info = { + sizeof(GstMD5SinkClass), + NULL, + NULL, + (GClassInitFunc) gst_md5sink_class_init, + NULL, + NULL, + sizeof(GstMD5Sink), + 0, + (GInstanceInitFunc) gst_md5sink_init, + }; + md5sink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstMD5Sink", &md5sink_info, 0); + } + return md5sink_type; +} + +static void +gst_md5sink_class_init (GstMD5SinkClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MD5, + g_param_spec_pointer ("md5", "md5", "current value of the md5 sum", + G_PARAM_READABLE)); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_md5sink_change_state); + + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_md5sink_get_property); +} + +static void +gst_md5sink_init (GstMD5Sink *md5sink) +{ + GstPad *pad; + pad = gst_pad_new_from_template (GST_PADTEMPLATE_GET (md5_sink_factory), "sink"); + gst_element_add_pad (GST_ELEMENT (md5sink), pad); + gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_md5sink_chain)); + + md5_init_ctx (md5sink); +} + +static GstElementStateReturn +gst_md5sink_change_state (GstElement *element) +{ + GstMD5Sink *sink; + + /* element check */ + sink = GST_MD5SINK (element); + g_return_val_if_fail (sink != NULL, GST_PAD_CONNECT_REFUSED); + g_return_val_if_fail (GST_IS_MD5SINK (sink), GST_PAD_CONNECT_REFUSED); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_READY_TO_PAUSED: + md5_init_ctx (sink); + break; + case GST_STATE_PAUSED_TO_READY: + md5_finish_ctx (sink, sink->md5); + break; + default: + break; + } + + if ((GST_ELEMENT_CLASS (parent_class)->change_state)) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; +} + +static void +gst_md5sink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstMD5Sink *sink; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_MD5SINK (object)); + + sink = GST_MD5SINK (object); + + switch (prop_id) { + case ARG_MD5: + md5_read_ctx (sink, sink->md5); + g_value_set_pointer (value, sink->md5); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_md5sink_chain (GstPad *pad, GstBuffer *buf) +{ + GstMD5Sink *md5sink; + + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (buf != NULL); + + md5sink = GST_MD5SINK (gst_pad_get_parent (pad)); + + if (GST_IS_BUFFER (buf)) + { + md5_process_bytes (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), md5sink); + } + + gst_buffer_unref (buf); +} + +GstElementDetails gst_md5sink_details = { + "MD5 Sink", + "Sink", + "compute MD5 for incoming data", + VERSION, + "Benjamin Otte ", + "(C) 2002", +}; + +gboolean +gst_md5sink_factory_init (GstElementFactory *factory) +{ + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (md5_sink_factory)); + + return TRUE; +} + diff --git a/plugins/elements/gstmd5sink.h b/plugins/elements/gstmd5sink.h new file mode 100644 index 0000000000..7d3377ce5f --- /dev/null +++ b/plugins/elements/gstmd5sink.h @@ -0,0 +1,86 @@ +/* GStreamer + * Copyright (C) 2002 Erik Walthinsen + * 2002 Wim Taymans + * + * gstmd5sink.h: + * + * 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_MD5SINK_H__ +#define __GST_MD5SINK_H__ + + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +GstElementDetails gst_md5sink_details; + + +#define GST_TYPE_MD5SINK \ + (gst_md5sink_get_type()) +#define GST_MD5SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MD5SINK,GstMD5Sink)) +#define GST_MD5SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MD5SINK,GstMD5SinkClass)) +#define GST_IS_MD5SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MD5SINK)) +#define GST_IS_MD5SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MD5SINK)) + +typedef struct _GstMD5Sink GstMD5Sink; +typedef struct _GstMD5SinkClass GstMD5SinkClass; + +struct _GstMD5Sink { + GstElement element; + + /* md5 information */ + guint32 A; + guint32 B; + guint32 C; + guint32 D; + + guint32 total[2]; + guint32 buflen; + gchar buffer[128] __attribute__ ((__aligned__ (__alignof__ (guint32)))); + + /* latest md5 */ + guchar md5[16]; + +}; + +struct _GstMD5SinkClass { + GstElementClass parent_class; + +}; + +GType gst_md5sink_get_type (void); + +gboolean gst_md5sink_factory_init (GstElementFactory *factory); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_MD5SINK_H__ */