From: Benjamin Otte Date: Tue, 28 Oct 2003 20:52:41 +0000 (+0000) Subject: merge TYPEFIND branch. Major changes: X-Git-Tag: 1.19.3~511^2~15203 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=112b1959da597179354a49397afb3604ff215449;p=platform%2Fupstream%2Fgstreamer.git merge TYPEFIND branch. Major changes: Original commit message from CVS: merge TYPEFIND branch. Major changes: - totally reworked type(find) system - all typefind functions are in gst/typefind now - more typefind functions then before - some plugins might fail to compile now because I don't have them installed and they a) require bytestream or b) haven't had their typefind fixed. Please fix those plugins and put the typefind functions into gst/typefind if they don't have dependencies --- diff --git a/configure.ac b/configure.ac index 47c08c3..02f2636 100644 --- a/configure.ac +++ b/configure.ac @@ -264,10 +264,10 @@ GST_PLUGINS_ALL="\ cutter debug deinterlace effectv festival \ filter flx goom id3 intfloat law level median mixmatrix \ mpeg1sys mpeg1videoparse mpeg2enc mpeg2sub \ - mpegaudio mpegaudioparse mpegstream mpegtypes \ + mpegaudio mpegaudioparse mpegstream \ monoscope oneton overlay passthrough playondemand qtdemux \ realmedia rtp rtjpeg silence sine smooth smpte \ - spectrum speed stereo synaesthesia tcp udp vbidec \ + spectrum speed stereo synaesthesia tcp typefind udp vbidec \ videocrop videodrop videofilter videoflip videoscale \ videotestsrc volenv volume wavenc wavparse y4m" @@ -1193,7 +1193,6 @@ gst/mpeg2sub/Makefile gst/mpegaudio/Makefile gst/mpegaudioparse/Makefile gst/mpegstream/Makefile -gst/mpegtypes/Makefile gst/modplug/Makefile gst/modplug/libmodplug/Makefile gst/monoscope/Makefile @@ -1214,6 +1213,7 @@ gst/speed/Makefile gst/stereo/Makefile gst/synaesthesia/Makefile gst/tcp/Makefile +gst/typefind/Makefile gst/udp/Makefile gst/vbidec/Makefile gst/videocrop/Makefile diff --git a/ext/vorbis/vorbis.c b/ext/vorbis/vorbis.c index fc6b75e..8a0cff9 100644 --- a/ext/vorbis/vorbis.c +++ b/ext/vorbis/vorbis.c @@ -25,8 +25,6 @@ extern GType vorbisfile_get_type(void); extern GstElementDetails vorbisfile_details; extern GstElementDetails vorbisenc_details; -static GstCaps* vorbis_type_find (GstByteStream *bs, gpointer private); - GstPadTemplate *gst_vorbisdec_src_template, *gst_vorbisdec_sink_template; GstPadTemplate *gst_vorbisenc_src_template, *gst_vorbisenc_sink_template; @@ -73,44 +71,15 @@ raw_caps2_factory (void) NULL)); } -static GstTypeDefinition vorbisdefinition = { - "vorbis_audio/x-ogg", - "application/ogg", - ".ogg", - vorbis_type_find, -}; - -static GstCaps* -vorbis_type_find (GstByteStream *bs, gpointer private) -{ - GstBuffer *buf = NULL; - GstCaps *new = NULL; - - if (gst_bytestream_peek (bs, &buf, 4) == 4) { - guint32 head = GUINT32_FROM_BE (*((guint32 *) GST_BUFFER_DATA (buf))); - - if (head == 0x4F676753) { - new = GST_CAPS_NEW ("vorbis_type_find", - "application/ogg", - NULL); - } - } - - if (buf != NULL) { - gst_buffer_unref (buf); - } - - return new; -} - - static gboolean plugin_init (GModule *module, GstPlugin *plugin) { GstElementFactory *enc, *file; - GstTypeFactory *type; GstCaps *raw_caps, *vorbis_caps, *raw_caps2; + if (!gst_library_load ("gstbytestream")) + return FALSE; + gst_plugin_set_longname (plugin, "The OGG Vorbis Codec"); /* create an elementfactory for the vorbisenc element */ @@ -158,9 +127,6 @@ plugin_init (GModule *module, GstPlugin *plugin) gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (file)); - type = gst_type_factory_new (&vorbisdefinition); - gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type)); - return TRUE; } diff --git a/ext/vorbis/vorbisfile.c b/ext/vorbis/vorbisfile.c index 4e519a9..98978c1 100644 --- a/ext/vorbis/vorbisfile.c +++ b/ext/vorbis/vorbisfile.c @@ -24,7 +24,10 @@ #include #include #include -#include +#include + +GST_DEBUG_CATEGORY_STATIC (vorbisfile_debug); +#define GST_CAT_DEFAULT vorbisfile_debug #define GST_TYPE_VORBISFILE \ (vorbisfile_get_type()) @@ -168,6 +171,8 @@ vorbisfile_get_type (void) &vorbisfile_info, 0); logical_stream_format = gst_format_register ("logical_stream", "The logical stream"); + + GST_DEBUG_CATEGORY_INIT (vorbisfile_debug, "vorbisfile", 0, "vorbis in ogg decoding element"); } return vorbisfile_type; } @@ -229,6 +234,8 @@ gst_vorbisfile_init (VorbisFile * vorbisfile) vorbisfile->streaminfo = NULL; vorbisfile->current_link = -1; vorbisfile->blocksize = DEFAULT_BLOCKSIZE; + + GST_FLAG_SET (vorbisfile, GST_ELEMENT_EVENT_AWARE); } /* the next four functions are the ov callbacks we provide to vorbisfile diff --git a/gst-libs/ext/ffmpeg/Tag b/gst-libs/ext/ffmpeg/Tag index 01fdcac..f6ecaeb 100644 --- a/gst-libs/ext/ffmpeg/Tag +++ b/gst-libs/ext/ffmpeg/Tag @@ -1 +1 @@ -D2003.10.26.10.00.00 +2003-10-26 10:00 GMT diff --git a/gst/adder/gstadder.c b/gst/adder/gstadder.c index 7f1c2e6..a6d2145 100644 --- a/gst/adder/gstadder.c +++ b/gst/adder/gstadder.c @@ -603,6 +603,9 @@ plugin_init (GModule *module, GstPlugin *plugin) { GstElementFactory *factory; + if (!gst_library_load ("gstbytestream")) + return FALSE; + factory = gst_element_factory_new ("adder", GST_TYPE_ADDER, &adder_details); g_return_val_if_fail (factory != NULL, FALSE); diff --git a/gst/adder/gstadder.h b/gst/adder/gstadder.h index 570684b..4ae8a6b 100644 --- a/gst/adder/gstadder.h +++ b/gst/adder/gstadder.h @@ -24,7 +24,7 @@ #define __GST_ADDER_H__ #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/gst/typefind/Makefile.am b/gst/typefind/Makefile.am new file mode 100644 index 0000000..fa729a2 --- /dev/null +++ b/gst/typefind/Makefile.am @@ -0,0 +1,8 @@ + +plugin_LTLIBRARIES = libgsttypefindfunctions.la + +libgsttypefindfunctions_la_SOURCES = gsttypefindfunctions.c +libgsttypefindfunctions_la_CFLAGS = $(GST_CFLAGS) +libgsttypefindfunctions_la_LIBADD = +libgsttypefindfunctions_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + diff --git a/gst/typefind/gsttypefindfunctions.c b/gst/typefind/gsttypefindfunctions.c new file mode 100644 index 0000000..a9cb457 --- /dev/null +++ b/gst/typefind/gsttypefindfunctions.c @@ -0,0 +1,940 @@ +/* GStreamer + * Copyright (C) 2003 Benjamion Otte + * + * gsttypefindfunctions.c: collection of various typefind functions + * + * 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 + +#include +#include +#include +#include + +#include +#include + +GST_DEBUG_CATEGORY_STATIC (type_find_debug); +#define GST_CAT_DEFAULT type_find_debug + +/*** video/x-ms-asf ***********************************************************/ + +#define ASF_CAPS gst_caps_new ("asf_type_find", "video/x-ms-asf", NULL) +static void +asf_type_find (GstTypeFind *tf, gpointer unused) +{ + static guint8 header_guid[16] = + {0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, + 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}; + guint8 *data = gst_type_find_peek (tf, 0, 16); + + if (data && memcmp (data, header_guid, 16) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, ASF_CAPS); + } +} + +/*** audio/x-au ***************************************************************/ + +#define AU_CAPS gst_caps_new ("au_type_find", "audio/x-au", NULL) +static void +au_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 4); + + if (data && memcmp (data, ".snd", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, AU_CAPS); + } +}; + +/*** video/avi ****************************************************************/ + +#define AVI_CAPS GST_CAPS_NEW ("avi_type_find", "video/avi", NULL) +static void +avi_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 12); + + if (data && memcmp (data, "RIFF", 4) == 0) { + data += 8; + if (memcmp (data, "AVI ", 4) == 0) + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, AVI_CAPS); + } +} + +/*** video/x-cdxa ****************************************************************/ + +#define CDXA_CAPS GST_CAPS_NEW ("cdxa_type_find", "video/x-cdxa", NULL) +static void +cdxa_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 4); + + if (data && memcmp (data, "RIFF", 4) == 0) { + data += 8; + if (memcmp (data, "CDXA", 4) == 0) + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, CDXA_CAPS); + } +} + +/*** text/plain ****************************************************************/ + +#define UTF8_CAPS GST_CAPS_NEW ("cdxa_type_find", "text/plain", NULL) +static void +utf8_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data; + /* randomly decided values */ + guint size = 1024; /* starting size */ + guint probability = 95; /* starting probability */ + guint step = 10; /* how much we reduce probability in each iteration */ + + while (probability > step) { + data = gst_type_find_peek (tf, 0, size); + if (data) { + gchar *end; + gchar *start = (gchar *) data; + + if (g_utf8_validate (start, size, (const gchar **) &end) || + (end - start + 4 > size)) { /* allow last char to be cut off */ + gst_type_find_suggest (tf, probability, UTF8_CAPS); + } + return; + } + size /= 2; + probability -= step; + } +} + +/*** text/uri-list ************************************************************/ + +#define URI_CAPS GST_CAPS_NEW ("uri_type_find", "text/uri-list", NULL) +#define BUFFER_SIZE 16 /* If the string is < 16 bytes we're screwed */ +#define INC_BUFFER { \ + pos++; \ + if (pos == BUFFER_SIZE) { \ + pos = 0; \ + offset += BUFFER_SIZE; \ + data = gst_type_find_peek (tf, offset, BUFFER_SIZE); \ + if (data == NULL) return; \ + } else { \ + data++; \ + } \ +} +static void +uri_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, BUFFER_SIZE); + guint pos = 0; + guint offset = 0; + + if (data) { + /* Search for # comment lines */ + while (*data == '#') { + /* Goto end of line */ + while (*data != '\n') { + INC_BUFFER; + } + + INC_BUFFER; + } + + if (!g_ascii_isalpha (*data)) { + /* Had a non alpha char - can't be uri-list */ + return; + } + + INC_BUFFER; + + while (g_ascii_isalnum (*data)) { + INC_BUFFER; + } + + if (*data != ':') { + /* First non alpha char is not a : */ + return; + } + + /* Get the next 2 bytes as well */ + data = gst_type_find_peek (tf, offset + pos, 3); + if (data == NULL) return; + + if (data[1] != '/' && data[2] != '/') { + return; + } + + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, URI_CAPS); + } +} + +/*** video/x-fli **************************************************************/ + +#define FLX_CAPS GST_CAPS_NEW ("flx_type_find", "video/x-fli", NULL) +static void +flx_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8* data = gst_type_find_peek (tf, 0, 134); + + if (data) { + /* check magic and the frame type of the first frame */ + if ((data[4] == 0x11 || data[4] == 0x12 || + data[4] == 0x30 || data[4] == 0x44) && + data[5] == 0xaf && + ((data[132] == 0x00 || data[132] == 0xfa) && data[133] == 0xf1)) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, FLX_CAPS); + } + return; + } + data = gst_type_find_peek (tf, 0, 6); + if (data) { + /* check magic only */ + if ((data[4] == 0x11 || data[4] == 0x12 || + data[4] == 0x30 || data[4] == 0x44) && + data[5] == 0xaf) { + gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, FLX_CAPS); + } + return; + } +} + +/*** application/x-id3 **************************************************************/ + +#define ID3_CAPS GST_CAPS_NEW ("id3_type_find", "application/x-id3", NULL) +static void +id3_type_find (GstTypeFind *tf, gpointer unused) +{ + /* detect ID3v2 first */ + guint8* data = gst_type_find_peek (tf, 0, 10); + if (data) { + /* detect valid header */ + if (memcmp (data, "ID3", 3) == 0 && + data[3] != 0xFF && data[4] != 0xFF && + (data[6] & 0x80) == 0 && (data[7] & 0x80) == 0 && + (data[8] & 0x80) == 0 && (data[9] & 0x80) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, ID3_CAPS); + return; + } + } + data = gst_type_find_peek (tf, -128, 3); + if (data && memcmp (data, "TAG", 3) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, ID3_CAPS); + } +} + +/*** audio/mpeg **************************************************************/ + +/** + * The chance that random data is identified as a valid mp3 header is 63 / 2^18 + * (0.024%) per try. This makes the function for calculating false positives + * 1 - (1 - ((63 / 2 ^18) ^ GST_MP3_TYPEFIND_MIN_HEADERS)) ^ buffersize) + * This has the following probabilities of false positives: + * datasize MIN_HEADERS + * (bytes) 1 2 3 4 + * 4096 62.6% 0.02% 0% 0% + * 16384 98% 0.09% 0% 0% + * 1 MiB 100% 5.88% 0% 0% + * 1 GiB 100% 100% 1.44% 0% + * 1 TiB 100% 100% 100% 0.35% + * This means that the current choice (3 headers by most of the time 4096 byte + * buffers is pretty safe for now. + * + * The max. size of each frame is 1440 bytes, which means that for N frames to + * be detected, we need 1440 * GST_MP3_TYPEFIND_MIN_HEADERS + 3 bytes of data. + * Assuming we step into the stream right after the frame header, this + * means we need 1440 * (GST_MP3_TYPEFIND_MIN_HEADERS + 1) - 1 + 3 bytes + * of data (5762) to always detect any mp3. + */ + +static guint mp3types_bitrates[2][3][16] = +{ { {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, }, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, }, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, } }, + { {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, }, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, }, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, } }, +}; + +static guint mp3types_freqs[3][3] = +{ {11025, 12000, 8000}, + {22050, 24000, 16000}, + {44100, 48000, 32000}}; + +static inline guint +mp3_type_frame_length_from_header (guint32 header, guint *put_layer, + guint *put_channels, guint *put_bitrate, + guint *put_samplerate) +{ + guint length; + gulong mode, samplerate, bitrate, layer, version, channels; + + if ((header & 0xffe00000) != 0xffe00000) + return 0; + + /* we don't need extension, copyright, original or + * emphasis for the frame length */ + header >>= 6; + + /* mode */ + mode = header & 0x3; + header >>= 3; + + /* padding */ + length = header & 0x1; + header >>= 1; + + /* sampling frequency */ + samplerate = header & 0x3; + if (samplerate == 3) + return 0; + header >>= 2; + + /* bitrate index */ + bitrate = header & 0xF; + if (bitrate == 15 || bitrate == 0) + return 0; + + /* ignore error correction, too */ + header >>= 5; + + /* layer */ + layer = 4 - (header & 0x3); + if (layer == 4) + return 0; + header >>= 2; + + /* version 0=MPEG2.5; 2=MPEG2; 3=MPEG1 */ + version = header & 0x3; + if (version == 1) + return 0; + + /* lookup */ + channels = (mode == 3) ? 1 : 2; + bitrate = mp3types_bitrates[version == 3 ? 0 : 1][layer - 1][bitrate]; + samplerate = mp3types_freqs[version > 0 ? version - 1 : 0][samplerate]; + + /* calculating */ + if (layer == 1) { + length = ((12000 * bitrate / samplerate) + length) * 4; + } else { + length += ((layer == 3 && version == 0) ? 72000 : 144000) * bitrate / samplerate; + } + + GST_LOG ("mp3typefind: alculated mp3 frame length of %u bytes", length); + GST_LOG ("mp3typefind: samplerate = %lu - bitrate = %lu - layer = %lu - version = %lu" + " - channels = %lu", + samplerate, bitrate, layer, version, channels); + + if (put_layer) + *put_layer = layer; + if (put_channels) + *put_channels = channels; + if (put_bitrate) + *put_bitrate = bitrate; + if (put_samplerate) + *put_samplerate = samplerate; + + return length; +} + + +#define MP3_CAPS(layer) (layer == 0 ? \ + GST_CAPS_NEW ("mp3_type_find", "audio/mpeg", \ + "mpegversion", GST_PROPS_INT (1), \ + "layer", GST_PROPS_INT_RANGE (1, 3)) : \ + GST_CAPS_NEW ("mp3_type_find", "audio/mpeg", \ + "mpegversion", GST_PROPS_INT (1), \ + "layer", GST_PROPS_INT (layer))) +/* + * random values for typefinding + * if no more data is available, we will return a probability of + * (found_headers/TRY_HEADERS) * (MAXIMUM * (TRY_SYNC - bytes_skipped) + * / TRY_SYNC) + */ +#define GST_MP3_TYPEFIND_TRY_HEADERS 5 +#define GST_MP3_TYPEFIND_TRY_SYNC (GST_TYPE_FIND_MAXIMUM * 100) /* 10kB */ +#define GST_MP3_TYPEFIND_SYNC_SIZE 2048 + +static void +mp3_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = NULL; + guint size = 0; + guint64 skipped = 0; + + while (skipped < GST_MP3_TYPEFIND_TRY_SYNC) { + if (size <= 0) { + data = gst_type_find_peek (tf, skipped, GST_MP3_TYPEFIND_SYNC_SIZE); + if (!data) + break; + size = GST_MP3_TYPEFIND_SYNC_SIZE; + } + if (*data == 0xFF) { + guint8* head_data; + guint layer, bitrate, samplerate, channels; + guint found = 0; /* number of valid headers found */ + guint64 offset = skipped; + + while (found < GST_MP3_TYPEFIND_TRY_HEADERS) { + guint32 head; + guint length; + guint prev_layer = 0, prev_bitrate = 0, + prev_channels = 0, prev_samplerate = 0; + + head_data = gst_type_find_peek (tf, offset, 4); + if (!head_data) + break; + head = GUINT32_FROM_BE(*((guint32 *) head_data)); + if (!(length = mp3_type_frame_length_from_header (head, &layer, + &channels, &bitrate, &samplerate))) { + break; + } + if ((prev_layer && prev_layer != layer) || + /* (prev_bitrate && prev_bitrate != bitrate) || <-- VBR */ + (prev_samplerate && prev_samplerate != samplerate) || + (prev_channels && prev_channels != channels)) { + /* this means an invalid property, or a change, which might mean + * that this is not a mp3 but just a random bytestream. It could + * be a freaking funky encoded mp3 though. We'll just not count + * this header*/ + prev_layer = layer; + prev_bitrate = bitrate; + prev_channels = channels; + prev_samplerate = samplerate; + } else { + found++; + } + offset += length; + } + g_assert (found <= GST_MP3_TYPEFIND_TRY_HEADERS); + if (found == GST_MP3_TYPEFIND_TRY_HEADERS || + head_data == NULL) { + /* we can make a valid guess */ + guint probability = found * GST_TYPE_FIND_MAXIMUM * + (GST_MP3_TYPEFIND_TRY_SYNC - skipped) / + GST_MP3_TYPEFIND_TRY_HEADERS / GST_MP3_TYPEFIND_TRY_SYNC; + if (probability < GST_TYPE_FIND_MINIMUM) + probability = GST_TYPE_FIND_MINIMUM; + + /* make sure we're not id3 tagged */ + head_data = gst_type_find_peek (tf, -128, 3); + if (!head_data) { + probability = probability * 4 / 5; + } else if (memcmp (head_data, "TAG", 3) == 0) { + probability = 0; + } + g_assert (probability <= GST_TYPE_FIND_MAXIMUM); + if (probability > 0) { + g_assert (layer > 0); + gst_type_find_suggest (tf, probability, MP3_CAPS (layer)); + } + return; + } + } + data++; + skipped++; + size--; + } +} + +/*** video/mpeg systemstream **************************************************/ + +#define MPEG_SYS_CAPS(version) (version == 0 ? \ + GST_CAPS_NEW ("mpeg_type_find", "video/mpeg", \ + "systemstream", GST_PROPS_BOOLEAN (TRUE), \ + "mpegversion", GST_PROPS_INT_RANGE (1, 2)) : \ + GST_CAPS_NEW ("mpeg_type_find", "video/mpeg", \ + "systemstream", GST_PROPS_BOOLEAN (TRUE), \ + "mpegversion", GST_PROPS_INT (version))) +#define IS_MPEG_HEADER(data) ((((guint8 *)data)[0] == 0x00) && \ + (((guint8 *)data)[1] == 0x00) && \ + (((guint8 *)data)[2] == 0x01) && \ + (((guint8 *)data)[3] == 0xBA)) +#define IS_MPEG_SYSTEM_HEADER(data) ((((guint8 *)data)[0] == 0x00) && \ + (((guint8 *)data)[1] == 0x00) && \ + (((guint8 *)data)[2] == 0x01) && \ + (((guint8 *)data)[3] == 0xBB)) +#define IS_MPEG_PACKET_HEADER(data) ((((guint8 *)data)[0] == 0x00) && \ + (((guint8 *)data)[1] == 0x00) && \ + (((guint8 *)data)[2] == 0x01) && \ + ((((guint8 *)data)[3] & 0x80) == 0x80)) +static void +mpeg2_sys_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 5); + + if (data && IS_MPEG_HEADER (data)) { + if ((data[4] & 0xC0) == 0x40) { + /* type 2 */ + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MPEG_SYS_CAPS (2)); + } + } +}; +/* ATTANTION: ugly return value: + * 0 - invalid data + * 1 - not enough data + * anything else - size until next package + */ +static guint +mpeg1_parse_header (GstTypeFind *tf, guint64 offset) +{ + guint8 *data = gst_type_find_peek (tf, offset, 18); + guint size; + + if (!data) { + GST_LOG ("couldn't get 18 bytes to parse MPEG header"); + return 1; + } + + /* check header */ + if (!IS_MPEG_HEADER (data)) { + GST_LOG ("This isn't an MPEG header"); + return 0; + } + data += 4; + + /* check marker bits */ + if ((*data & 0xF1) != 0x21) { + GST_LOG ("marker bits in byte 4 don't match"); + return 0; + } + data += 2; + if ((*data & 0x01) != 0x01) { + GST_LOG ("marker bits in byte 6 don't match"); + return 0; + } + data += 2; + if ((*data & 0x01) != 0x01) { + GST_LOG ("marker bits in byte 8 don't match"); + return 0; + } + data ++; + if ((*data & 0x80) != 0x80) { + GST_LOG ("marker bits in byte 9 don't match"); + return 0; + } + data += 2; + if ((*data & 0x01) != 0x01) { + GST_LOG ("marker bits in byte 11 don't match"); + return 0; + } + data++; + + if (!IS_MPEG_PACKET_HEADER (data) && + !IS_MPEG_SYSTEM_HEADER (data)) { + GST_LOG ("MPEG packet header doesn't match: %8.8X", GUINT32_FROM_BE (*((guint32 *) data))); + return 0; + } + + data += 4; + + size = GUINT16_FROM_BE (*((guint16 *) data)) + 18; + + GST_DEBUG ("found mpeg1 packet at offset %"G_GUINT64_FORMAT" with size %u", offset, size); + return size; +} +/* calculation of possibility to identify random data as mpeg systemstream: + * bits that must match in header detection: 65 + * chance that random data is identifed: 1/2^65 + * chance that GST_MPEG_TYPEFIND_TRY_HEADERS headers are identified: + * 1/2^(65*GST_MPEG_TYPEFIND_TRY_HEADERS) + * chance that this happens in GST_MPEG_TYPEFIND_TRY_SYNC bytes: + * 1-(1-1/2^(65*GST_MPEG_TYPEFIND_TRY_HEADERS))^GST_MPEG_TYPEFIND_TRY_SYNC + * for current values: + * 1-(1-1/2^(65*2)^50000 + * and that value is way smaller than 0,1% + */ +#define GST_MPEG_TYPEFIND_TRY_HEADERS 2 +#define GST_MPEG_TYPEFIND_TRY_SYNC (GST_TYPE_FIND_MAXIMUM * 500) /* 50kB */ +#define GST_MPEG_TYPEFIND_SYNC_SIZE 2048 +static void +mpeg1_sys_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data; + guint size = 0; + guint64 skipped = 0; + + while (skipped < GST_MPEG_TYPEFIND_TRY_SYNC) { + if (size < 4) { + data = gst_type_find_peek (tf, skipped, GST_MPEG_TYPEFIND_SYNC_SIZE); + if (!data) + break; + size = GST_MPEG_TYPEFIND_SYNC_SIZE; + } + if (IS_MPEG_HEADER (data)) { + /* found packet start code */ + guint found = 0; + guint packet_size; + guint64 offset = skipped; + + while (found < GST_MPEG_TYPEFIND_TRY_HEADERS) { + packet_size = mpeg1_parse_header (tf, offset); + if (packet_size <= 1) + break; + offset += packet_size; + found++; + } + g_assert (found <= GST_MPEG_TYPEFIND_TRY_HEADERS); + if (found == GST_MPEG_TYPEFIND_TRY_HEADERS || + packet_size == 1) { + guint probability = found * GST_TYPE_FIND_MAXIMUM * + (GST_MPEG_TYPEFIND_TRY_SYNC - skipped) / + GST_MPEG_TYPEFIND_TRY_HEADERS / GST_MPEG_TYPEFIND_TRY_SYNC; + + if (probability < GST_TYPE_FIND_MINIMUM) + probability = GST_TYPE_FIND_MINIMUM; + g_assert (probability <= GST_TYPE_FIND_MAXIMUM); + gst_type_find_suggest (tf, probability, MPEG_SYS_CAPS (1)); + return; + } + } + data++; + skipped++; + size--; + } +} + +/*** video/quicktime***********************************************************/ + +#define QT_CAPS gst_caps_new ("qt_typefind", "video/quicktime", NULL) +static void +qt_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data; + guint tip = 0; + guint64 offset = 0; + + while ((data = gst_type_find_peek (tf, offset, 8)) != NULL) { + if (strncmp (&data[4], "wide", 4) != 0 && + strncmp (&data[4], "moov", 4) != 0 && + strncmp (&data[4], "mdat", 4) != 0 && + strncmp (&data[4], "free", 4) != 0) { + tip = 0; + break; + } + if (tip == 0) { + tip = GST_TYPE_FIND_LIKELY; + } else { + tip = GST_TYPE_FIND_MAXIMUM; + break; + } + offset += GUINT32_FROM_BE (*((guint32 *) data)); + } + if (tip > 0) { + gst_type_find_suggest (tf, tip, QT_CAPS); + } +}; + +/*** application/vnd.rn-realmedia *********************************************/ + +#define RM_CAPS gst_caps_new ("realmedia_type_find", "application/vnd.rn-realmedia", NULL) +static void +rm_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 4); + + if (data && memcmp (data, ".RMF", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, RM_CAPS); + } +}; + +/*** audio/x-wav ****************************************************************/ + +#define WAV_CAPS GST_CAPS_NEW ("wav_type_find", "audio/x-wav", NULL) +static void +wav_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 12); + + if (data && memcmp (data, "RIFF", 4) == 0) { + data += 8; + if (memcmp (data, "WAVE", 4) == 0) + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, WAV_CAPS); + } +} + +/*** audio/x-aiff *********************************************/ + +#define AIFF_CAPS GST_CAPS_NEW ("aiff_type_find", "audio/x-aiff", NULL) +static void +aiff_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 4); + + if (data && memcmp (data, "FORM", 4) == 0) { + data += 8; + if (memcmp (data, "AIFF", 4) == 0) + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, AIFF_CAPS); + } +} + +/*** audio/x-shorten ****************************************/ + +#define SHN_CAPS GST_CAPS_NEW ("shn_type_find", "audio/x-shorten", NULL) +static void +shn_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, -8, 8); + + if (data && memcmp (data, "SHNAMPSK", 8) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, SHN_CAPS); + } +} + +/*** audio/x-mod *********************************************/ + +#define MOD_CAPS gst_caps_new ("mod_type_find", "audio/x-mod", NULL) +/* FIXME: M15 CheckType to do */ +static void +mod_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data; + + /* MOD */ + if ((data = gst_type_find_peek (tf, 1080, 4)) != NULL) { + /* Protracker and variants */ + if ((memcmp(data, "M.K.", 4) == 0) || + (memcmp(data, "M!K!", 4) == 0) || + /* Star Tracker */ + (memcmp(data, "FLT", 3) == 0 && isdigit (data[3])) || + (memcmp(data, "EXO", 3) == 0 && isdigit (data[3])) || + /* Oktalyzer (Amiga) */ + (memcmp(data, "OKTA", 4) == 0) || + /* Oktalyser (Atari) */ + (memcmp(data, "CD81", 4) == 0) || + /* Fasttracker */ + (memcmp(data + 1, "CHN", 3) == 0 && isdigit (data[0])) || + /* Fasttracker or Taketracker */ + (memcmp(data + 2, "CH", 2) == 0 && isdigit (data[0]) && isdigit (data[1])) || + (memcmp(data + 2, "CN", 2) == 0 && isdigit (data[0]) && isdigit (data[1]))) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + } + /* XM */ + if ((data = gst_type_find_peek (tf, 0, 38)) != NULL) { + if (memcmp(data, "Extended Module: ", 17) == 0 && + data[37] == 0x1A) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + } + /* OKT */ + if (data || (data = gst_type_find_peek (tf, 0, 8)) != NULL) { + if (memcmp(data, "OKTASONG", 8) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + } + if (data || (data = gst_type_find_peek (tf, 0, 4)) != NULL) { + /* 669 */ + if ((memcmp(data, "if", 2) == 0) || + (memcmp(data, "JN", 2) == 0)) { + gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, MOD_CAPS); + return; + } + /* AMF */ + if ((memcmp(data, "AMF", 3) == 0 && data[3] > 10 && data[3] < 14) || + /* IT */ + (memcmp(data, "IMPM", 4) == 0) || + /* MED */ + (memcmp(data, "MMD0", 4) == 0) || + (memcmp(data, "MMD1", 4) == 0) || + /* MTM */ + (memcmp(data, "MTM", 3) == 0)){ + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + /* DSM */ + if (memcmp(data, "RIFF", 4) == 0) { + guint8 *data2 = gst_type_find_peek (tf, 8, 4); + if (data2) { + if (memcmp (data2, "DSMF", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + } + } + /* FAM */ + if (memcmp(data, "FAM\xFE", 4) == 0) { + guint8 *data2 = gst_type_find_peek (tf, 44, 3); + if (data2) { + if (memcmp (data2, "compare", 3) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + } else { + gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, MOD_CAPS); + return; + } + } + /* GDM */ + if (memcmp(data, "GDM\xFE", 4) == 0) { + guint8 *data2 = gst_type_find_peek (tf, 71, 4); + if (data2) { + if (memcmp (data2, "GMFS", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + } else { + gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, MOD_CAPS); + return; + } + } + } + /* IMF */ + if ((data = gst_type_find_peek (tf, 60, 4)) != NULL) { + if (memcmp(data, "IM10", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + } + /* S3M */ + if ((data = gst_type_find_peek (tf, 44, 4)) != NULL) { + if (memcmp(data, "SCRM", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MOD_CAPS); + return; + } + } +} + +/*** audio/x-flac *************************************************************/ + +#define FLAC_CAPS gst_caps_new ("flac_type_find", "audio/x-flac", NULL) +static void +flac_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 4); + + if (data && memcmp (data, "fLaC", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, FLAC_CAPS); + } +}; + +/*** application/x-shockwave-flash ********************************************/ + +#define SWF_CAPS gst_caps_new ("swf_type_find", "application/x-shockwave-flash", NULL) +static void +swf_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 4); + + if (data && (data[0] == 'F' || data[0] == 'C') && + data[1] == 'W' && data[2] == 'S') { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, SWF_CAPS); + } +}; + +/*** application/ogg **********************************************************/ + +#define OGG_CAPS gst_caps_new ("ogg_type_find", "application/ogg", NULL) +static void +ogg_type_find (GstTypeFind *tf, gpointer unused) +{ + guint8 *data = gst_type_find_peek (tf, 0, 4); + + if (data && memcmp (data, "OggS", 4) == 0) { + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, OGG_CAPS); + } +}; + +/*** plugin initialization ****************************************************/ + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + /* can't initialize this via a struct as caps can't be statically initialized */ + + /* note: asx/wax/wmx are XML files, asf doesn't handle them */ + static gchar * asf_exts[] = {"asf", "wm", "wma", "wmv", NULL}; + static gchar * au_exts[] = {"au", "snd", NULL}; + static gchar * avi_exts[] = {"avi", NULL}; + static gchar * cdxa_exts[] = {"dat", NULL}; + static gchar * flac_exts[] = {"flac", NULL}; + static gchar * flx_exts[] = {"flc", "fli", NULL}; + static gchar * id3_exts[] = {"mp3", "mp2", "mp1", "mpga", "ogg", "flac", NULL}; + static gchar * mod_exts[] = {"669", "amf", "dsm", "gdm", "far", "imf", + "it", "med", "mod", "mtm", "okt", "sam", + "s3m", "stm", "stx", "ult", "xm", NULL}; + static gchar * mp3_exts[] = {"mp3", "mp2", "mp1", "mpga", NULL}; + static gchar * mpeg_sys_exts[] = {"mpe", "mpeg", "mpg", NULL}; + static gchar * ogg_exts[] = {"ogg", NULL}; + static gchar * qt_exts[] = {"mov", NULL}; + static gchar * rm_exts[] = {"ra", "ram", "rm", NULL}; + static gchar * swf_exts[] = {"swf", "swfl", NULL}; + static gchar * utf8_exts[] = {"txt", NULL}; + static gchar * wav_exts[] = {"wav", NULL}; + static gchar * aiff_exts[] = {"aiff", "aif", "aifc", NULL}; + static gchar * shn_exts[] = {"shn", NULL}; + static gchar * uri_exts[] = {"ram", NULL}; + + GST_DEBUG_CATEGORY_INIT (type_find_debug, "typefindfunctions", GST_DEBUG_FG_GREEN | GST_DEBUG_BG_RED, "generic type find functions"); + + gst_type_find_factory_register (plugin, "video/x-ms-asf", GST_ELEMENT_RANK_SECONDARY, + asf_type_find, asf_exts, ASF_CAPS, NULL); + gst_type_find_factory_register (plugin, "audio/x-au", GST_ELEMENT_RANK_MARGINAL, + au_type_find, au_exts, AU_CAPS, NULL); + gst_type_find_factory_register (plugin, "video/avi", GST_ELEMENT_RANK_PRIMARY, + avi_type_find, avi_exts, AVI_CAPS, NULL); + gst_type_find_factory_register (plugin, "video/x-cdxa", GST_ELEMENT_RANK_SECONDARY, + cdxa_type_find, cdxa_exts, CDXA_CAPS, NULL); + gst_type_find_factory_register (plugin, "audio/x-flac", GST_ELEMENT_RANK_PRIMARY, + flac_type_find, flac_exts, FLAC_CAPS, NULL); + gst_type_find_factory_register (plugin, "video/x-fli", GST_ELEMENT_RANK_MARGINAL, + flx_type_find, flx_exts, FLX_CAPS, NULL); + gst_type_find_factory_register (plugin, "application/x-id3", GST_ELEMENT_RANK_PRIMARY, + id3_type_find, id3_exts, ID3_CAPS, NULL); + gst_type_find_factory_register (plugin, "audio/x-mod", GST_ELEMENT_RANK_SECONDARY, + mod_type_find, mod_exts, MOD_CAPS, NULL); + gst_type_find_factory_register (plugin, "audio/mpeg", GST_ELEMENT_RANK_PRIMARY, + mp3_type_find, mp3_exts, MP3_CAPS (0), NULL); + gst_type_find_factory_register (plugin, "video/mpeg1", GST_ELEMENT_RANK_PRIMARY, + mpeg1_sys_type_find, mpeg_sys_exts, MPEG_SYS_CAPS (1), NULL); + gst_type_find_factory_register (plugin, "video/mpeg2", GST_ELEMENT_RANK_SECONDARY, + mpeg2_sys_type_find, mpeg_sys_exts, MPEG_SYS_CAPS (2), NULL); + gst_type_find_factory_register (plugin, "application/ogg", GST_ELEMENT_RANK_PRIMARY, + ogg_type_find, ogg_exts, OGG_CAPS, NULL); + gst_type_find_factory_register (plugin, "video/quicktime", GST_ELEMENT_RANK_SECONDARY, + qt_type_find, qt_exts, QT_CAPS, NULL); + gst_type_find_factory_register (plugin, "application/vnd.rn-realmedia", GST_ELEMENT_RANK_SECONDARY, + rm_type_find, rm_exts, RM_CAPS, NULL); + gst_type_find_factory_register (plugin, "application/x-shockwave-flash", GST_ELEMENT_RANK_SECONDARY, + swf_type_find, swf_exts, SWF_CAPS, NULL); + gst_type_find_factory_register (plugin, "text/plain", GST_ELEMENT_RANK_MARGINAL, + utf8_type_find, utf8_exts, UTF8_CAPS, NULL); + gst_type_find_factory_register (plugin, "text/uri-list", GST_ELEMENT_RANK_MARGINAL, + uri_type_find, uri_exts, URI_CAPS, NULL); + gst_type_find_factory_register (plugin, "audio/x-wav", GST_ELEMENT_RANK_SECONDARY, + wav_type_find, wav_exts, WAV_CAPS, NULL); + gst_type_find_factory_register (plugin, "audio/x-aiff", GST_ELEMENT_RANK_SECONDARY, + aiff_type_find, aiff_exts, AIFF_CAPS, NULL); + + gst_type_find_factory_register (plugin, "audio/x-shorten", GST_ELEMENT_RANK_SECONDARY, + shn_type_find, shn_exts, SHN_CAPS, NULL); + + return TRUE; +} +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "typefindfunctions", + plugin_init +}; +