Merge branch 'master' into 0.11

Conflicts:
	configure.ac
	ext/pango/gsttextoverlay.c
	ext/theora/gsttheoradec.c
	gst/adder/gstadder.c
	gst/adder/gstadder.h
	gst/audioresample/gstaudioresample.c
	gst/encoding/gstencodebin.c
	gst/playback/gstdecodebin.c
	gst/playback/gstdecodebin2.c
	tests/check/elements/decodebin2.c
	tests/check/elements/playbin-compressed.c
	win32/common/libgsttag.def
This commit is contained in:
Wim Taymans 2011-08-16 18:01:14 +02:00
commit 33467d9629
53 changed files with 12643 additions and 278 deletions

5
.gitignore vendored
View file

@ -43,8 +43,9 @@ Makefile
*.gir
*.typelib
gst-libs/gst/pbutils/gstpluginsbaseversion.h
gst-libs/gst/tag/mklangtables
/gst-libs/gst/pbutils/gstpluginsbaseversion.h
/gst-libs/gst/tag/mklangtables
/gst-libs/gst/tag/mklicensestables
tmp-orc.c
gst*orc.h

View file

@ -496,6 +496,20 @@ else
AM_CONDITIONAL(USE_ISO_CODES, false)
fi
dnl *** zlib is optionally used by id3 tag parsing in libgsttag ***
translit(dnm, m, l) AM_CONDITIONAL(USE_ZLIB, true)
AG_GST_CHECK_FEATURE(ZLIB, [zlib support for ID3 parsing in libgsttag],, [
PKG_CHECK_MODULES(ZLIB, [ zlib ], [
HAVE_ZLIB="yes"
], [
AG_GST_CHECK_LIBHEADER(ZLIB, z, uncompress,, zlib.h, [
HAVE_ZLIB="yes"
ZLIB_LIBS="-lz"
AC_SUBST(ZLIB_LIBS)
])
])
])
dnl *** sys plug-ins ***
echo

View file

@ -191,7 +191,9 @@
<xi:include href="xml/gsttagxmp.xml" />
<xi:include href="xml/gsttagxmpwriter.xml" />
<xi:include href="xml/gsttagdemux.xml" />
<xi:include href="xml/gsttagmux.xml" />
<xi:include href="xml/gsttaglanguagecodes.xml" />
<xi:include href="xml/gsttaglicenses.xml" />
</chapter>
<chapter id="gstreamer-base-utils">

View file

@ -1777,6 +1777,8 @@ gst_tag_from_id3_tag
gst_tag_from_id3_user_tag
gst_tag_to_id3_tag
gst_tag_list_add_id3_image
gst_tag_get_id3v2_tag_size
gst_tag_list_from_id3v2_tag
</SECTION>
<SECTION>
@ -1817,6 +1819,21 @@ gst_tag_demux_get_type
gst_tag_demux_result_get_type
</SECTION>
<SECTION>
<FILE>gsttagmux</FILE>
<INCLUDE>gst/tag/gsttagmux.h</INCLUDE>
GstTagMux
GstTagMuxClass
<SUBSECTION Standard>
GstTagMuxPrivate
GST_IS_TAG_MUX
GST_IS_TAG_MUX_CLASS
GST_TAG_MUX
GST_TAG_MUX_CLASS
GST_TYPE_TAG_MUX
gst_tag_demux_get_type
</SECTION>
<SECTION>
<FILE>gsttaglanguagecodes</FILE>
<INCLUDE>gst/tag/tag.h</INCLUDE>
@ -1828,6 +1845,20 @@ gst_tag_get_language_code_iso_639_2B
gst_tag_get_language_code_iso_639_2T
</SECTION>
<SECTION>
<FILE>gsttaglicenses</FILE>
<INCLUDE>gst/tag/tag.h</INCLUDE>
gst_tag_get_license_flags
gst_tag_get_license_nick
gst_tag_get_license_title
gst_tag_get_license_description
gst_tag_get_license_jurisdiction
gst_tag_get_license_version
gst_tag_get_licenses
<SUBSECTION Standard>
gst_tag_license_flags_get_type
</SECTION>
<SECTION>
<FILE>gsttagxmpwriter</FILE>
gst_tag_xmp_writer_add_all_schemas
@ -1940,6 +1971,7 @@ gst_codec_utils_aac_caps_set_level_and_profile
gst_codec_utils_h264_get_profile
gst_codec_utils_h264_get_level
gst_codec_utils_h264_caps_set_level_and_profile
gst_codec_utils_h264_get_level_idc
<SUBSECTION>
gst_codec_utils_mpeg4video_get_profile
gst_codec_utils_mpeg4video_get_level

View file

@ -785,7 +785,7 @@ packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
int size;
int duration;
if (packet->packet[0] & 1)
if (packet->bytes == 0 || packet->packet[0] & 1)
return 0;
mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);

View file

@ -66,6 +66,15 @@
* Uh? What are you talking about?
* I don&apos;t understand (18-62s)
* ]|
* One can also feed arbitrary live text into the element:
* |[
* gst-launch fdsrc fd=0 ! text/plain ! txt. videotestsrc ! \
* textoverlay name=txt shaded-background=yes font-desc="Serif 40" wait-text=false ! \
* xvimagesink
* ]| This shows new text as entered on the terminal (stdin). This is not suited
* for subtitles as the test overlay is not timed. Subtitles should use
* timestamped formats. For the above use case one can also read the text from
* the application as set the #GstTextOverlay:text property.
* </para>
* </refsect2>
*/

View file

@ -1539,10 +1539,11 @@ theora_dec_chain_reverse (GstTheoraDec * dec, gboolean discont, GstBuffer * buf)
dec->decode = g_list_prepend (dec->decode, gbuf);
/* if we copied a keyframe, flush and decode the decode queue */
gst_buffer_extract (gbuf, 0, data, 1);
if ((data[0] & 0x40) == 0) {
GST_DEBUG_OBJECT (dec, "copied keyframe");
res = theora_dec_flush_decode (dec);
if (gst_buffer_extract (gbuf, 0, data, 1) == 1) {
if ((data[0] & 0x40) == 0) {
GST_DEBUG_OBJECT (dec, "copied keyframe");
res = theora_dec_flush_decode (dec);
}
}
}
}

View file

@ -388,7 +388,7 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query)
if ((res =
gst_base_sink_query_latency (GST_BASE_SINK_CAST (basesink), &live,
&us_live, &min_l, &max_l))) {
GstClockTime min_latency, max_latency;
GstClockTime base_latency, min_latency, max_latency;
/* we and upstream are both live, adjust the min_latency */
if (live && us_live) {
@ -407,21 +407,25 @@ gst_base_audio_sink_query (GstElement * element, GstQuery * query)
basesink->priv->us_latency = min_l;
min_latency =
base_latency =
gst_util_uint64_scale_int (spec->seglatency * spec->segsize,
GST_SECOND, spec->rate * spec->bytes_per_sample);
GST_OBJECT_UNLOCK (basesink);
/* we cannot go lower than the buffer size and the min peer latency */
min_latency = min_latency + min_l;
min_latency = base_latency + min_l;
/* the max latency is the max of the peer, we can delay an infinite
* amount of time. */
max_latency = min_latency + (max_l == -1 ? 0 : max_l);
max_latency = (max_l == -1) ? -1 : (base_latency + max_l);
GST_DEBUG_OBJECT (basesink,
"peer min %" GST_TIME_FORMAT ", our min latency: %"
GST_TIME_FORMAT, GST_TIME_ARGS (min_l),
GST_TIME_ARGS (min_latency));
GST_DEBUG_OBJECT (basesink,
"peer max %" GST_TIME_FORMAT ", our max latency: %"
GST_TIME_FORMAT, GST_TIME_ARGS (max_l),
GST_TIME_ARGS (max_latency));
} else {
GST_DEBUG_OBJECT (basesink,
"peer or we are not live, don't care about latency");
@ -1577,6 +1581,12 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
else
render_stop = 0;
/* in some clock slaving cases, all late samples end up at 0 first,
* and subsequent ones align with that until threshold exceeded,
* and then sync back to 0 and so on, so avoid that altogether */
if (G_UNLIKELY (render_start == 0 && render_stop == 0))
goto too_late;
/* and bring the time to the rate corrected offset in the buffer */
render_start = gst_util_uint64_scale_int (render_start,
ringbuf->spec.rate, GST_SECOND);
@ -1703,6 +1713,11 @@ out_of_segment:
ret = GST_FLOW_OK;
goto done;
}
too_late:
{
GST_DEBUG_OBJECT (sink, "dropping late sample");
return GST_FLOW_OK;
}
/* ERRORS */
payload_failed:
{

View file

@ -37,6 +37,8 @@
#include "pbutils.h"
#include <string.h>
#define GST_SIMPLE_CAPS_HAS_NAME(caps,name) \
gst_structure_has_name(gst_caps_get_structure((caps),0),(name))
@ -526,6 +528,58 @@ gst_codec_utils_h264_get_level (const guint8 * sps, guint len)
}
}
/**
* gst_codec_utils_h264_get_level_idc:
* @level: A level string from caps
*
* Transform a level string from the caps into the level_idc
*
* Returns: the level_idc or 0 if the level is unknown
*
* Since: 0.10.36
*/
guint8
gst_codec_utils_h264_get_level_idc (const gchar * level)
{
g_return_val_if_fail (level != NULL, 0);
if (!strcmp (level, "1"))
return 10;
else if (!strcmp (level, "1b"))
return 9;
else if (!strcmp (level, "1.1"))
return 11;
else if (!strcmp (level, "1.2"))
return 12;
else if (!strcmp (level, "1.3"))
return 13;
else if (!strcmp (level, "2"))
return 20;
else if (!strcmp (level, "2.1"))
return 21;
else if (!strcmp (level, "2.2"))
return 22;
else if (!strcmp (level, "3"))
return 30;
else if (!strcmp (level, "3.1"))
return 31;
else if (!strcmp (level, "3.2"))
return 32;
else if (!strcmp (level, "4"))
return 40;
else if (!strcmp (level, "4.1"))
return 41;
else if (!strcmp (level, "4.2"))
return 42;
else if (!strcmp (level, "5"))
return 50;
else if (!strcmp (level, "5.1"))
return 51;
GST_WARNING ("Invalid level %s", level);
return 0;
}
/**
* gst_codec_utils_h264_caps_set_level_and_profile:
* @caps: the #GstCaps to which the level and profile are to be added
@ -658,13 +712,16 @@ gst_codec_utils_mpeg4video_get_profile (const guint8 * vis_obj_seq, guint len)
const gchar *
gst_codec_utils_mpeg4video_get_level (const guint8 * vis_obj_seq, guint len)
{
/* The profile/level codes are from 14496-2, table G-1, and the Wireshark
* sources: epan/dissectors/packet-mp4ves.c
/* The profile/level codes are from 14496-2, table G-1, the Wireshark
* sources: epan/dissectors/packet-mp4ves.c and the Xvid Sources:
* src/xvid.h.
* Levels 4a and 5 for SP were added in Amendment 2, level 6 in Amendment 4
* (see Xvid sources vfw/config.c)
*
* Each profile has a different maximum level it defines. Some of them still
* need special case handling, because not all levels start from 1, and the
* Simple profile defines an intermediate level as well. */
static const int level_max[] = { 3, 2, 2, 4, 2, 1, 2, 2, 2, 4, 3, 4, 2, 3, 4,
static const int level_max[] = { 6, 2, 2, 4, 2, 1, 2, 2, 2, 4, 3, 4, 2, 3, 4,
5
};
int profile_id, level_id;
@ -718,6 +775,9 @@ gst_codec_utils_mpeg4video_get_level (const guint8 * vis_obj_seq, guint len)
else if (profile_id == 0 && level_id == 9)
/* Simple Profile / Level 0b */
return "0b";
else if (profile_id == 0 && level_id == 4)
/* Simple Profile / Level 4a */
return "4a";
else if (profile_id == 0xf && level_id > 7)
/* Fine Granularity Scalable Profile */
return digit_to_string (level_id - 8);

View file

@ -44,6 +44,8 @@ const gchar * gst_codec_utils_h264_get_profile (const guint8 * sps, guint len);
const gchar * gst_codec_utils_h264_get_level (const guint8 * sps, guint len);
guint8 gst_codec_utils_h264_get_level_idc (const gchar * level);
gboolean gst_codec_utils_h264_caps_set_level_and_profile (GstCaps * caps,
const guint8 * sps,
guint len);

View file

@ -592,6 +592,14 @@ do_connect (const gchar * ip, guint16 port, GstPollFD * fdout,
getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &errno, &len);
#endif
goto sys_error;
} else {
#ifdef __APPLE__
/* osx wakes up select with POLLOUT if the connection is refused... */
socklen_t len = sizeof (errno);
getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &errno, &len);
if (errno != 0)
goto sys_error;
#endif
}
gst_poll_fd_ignored (fdset, fdout);

View file

@ -2,21 +2,26 @@ libgsttagincludedir = \
$(includedir)/gstreamer-@GST_MAJORMINOR@/gst/tag
libgsttaginclude_HEADERS = \
tag.h gsttagdemux.h xmpwriter.h
tag.h gsttagdemux.h gsttagmux.h xmpwriter.h
lib_LTLIBRARIES = libgsttag-@GST_MAJORMINOR@.la
libgsttag_@GST_MAJORMINOR@_la_SOURCES = \
gstvorbistag.c gstid3tag.c gstxmptag.c gstexiftag.c \
lang.c tags.c gsttagdemux.c gsttageditingprivate.c xmpwriter.c
libgsttag_@GST_MAJORMINOR@_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
libgsttag_@GST_MAJORMINOR@_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(LIBM)
gstvorbistag.c gstid3tag.c gstxmptag.c gstexiftag.c \
lang.c licenses.c tags.c gsttagdemux.c gsttagmux.c \
gsttageditingprivate.c id3v2.c id3v2frames.c xmpwriter.c
libgsttag_@GST_MAJORMINOR@_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) $(GST_CFLAGS) $(ZLIB_CFLAGS) \
-DLICENSE_TRANSLATIONS_PATH=\"$(pkgdatadir)/license-translations.dict\"
libgsttag_@GST_MAJORMINOR@_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(LIBM) $(ZLIB_LIBS)
libgsttag_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
# lang-tables.dat contains generated static data and is included by lang.c
EXTRA_DIST = lang-tables.dat
# licenses-tables.dat contains generated data and is included by licenses.c
EXTRA_DIST = lang-tables.dat licenses-tables.dat
noinst_HEADERS = gsttageditingprivate.h
noinst_HEADERS = gsttageditingprivate.h id3v2.h
if HAVE_INTROSPECTION
BUILT_GIRSOURCES = GstTag-@GST_MAJORMINOR@.gir
@ -71,16 +76,34 @@ endif
# little program that reads iso_639.xml and outputs tables for us as fallback
# for when iso-codes are not available (and so we don't have to read the xml
# just to map codes)
if USE_ISO_CODES
ISO_CODE_PROGS = mklangtables
mklangtables_SOURCES = mklangtables.c
mklangtables_CFLAGS = $(GST_CFLAGS)
mklangtables_LDADD = $(GST_LIBS)
else
ISO_CODE_PROGS =
endif
noinst_PROGRAMS = $(ISO_CODE_PROGS)
mklicensestables_SOURCES = mklicensestables.c
mklicensestables_CFLAGS = $(GST_CFLAGS)
mklicensestables_LDADD = $(GST_LIBS)
EXTRA_PROGRAMS = mklangtables mklicensestables
update-isocodes: mklangtables
$(builddir)/mklangtables > $(srcdir)/lang-tables.dat && \
echo "Updated lang-tables.dat"
update-licenses: mklicensestables
$(builddir)/mklicensestables \
--translation-dictionary=$(srcdir)/license-translations.dict \
> $(srcdir)/licenses-tables.dat && \
echo "Updated licenses-tables.dat and license-translations.dict"
#if USE_NLS
# Yes, this is not great, but it's only an implementation detail. The
# translations come from an external source here, so we don't want the
# strings retranslated, but also we don't want to create 30 1kB .mo files,
# so just do something for now, we can change it later if someone really cares.
licensetransdir = $(pkgdatadir)
licensetrans_DATA = $(builddir)/license-translations.dict
#endif
Android.mk: Makefile.am
androgenizer -:PROJECT libgsttag -:SHARED libgsttag-@GST_MAJORMINOR@ \

View file

@ -781,22 +781,41 @@ write_exif_ascii_tag (GstExifWriter * writer, guint16 tag, const gchar * str)
{
gint size;
guint32 offset = 0;
gchar *ascii_str;
gsize ascii_size;
GError *error = NULL;
size = strlen (str) + 1;
if (size > 4) {
ascii_str =
g_convert (str, size, "latin1", "utf8", NULL, &ascii_size, &error);
if (error) {
GST_WARNING ("Failed to convert exif tag to ascii: 0x%x - %s. Error: %s",
tag, str, error->message);
g_error_free (error);
g_free (ascii_str);
return;
}
/* add the \0 at the end */
ascii_size++;
if (ascii_size > 4) {
/* we only use the data offset here, later we add up the
* resulting tag headers offset and the base offset */
offset = gst_byte_writer_get_size (&writer->datawriter);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_ASCII,
size, offset, FALSE);
gst_byte_writer_put_string (&writer->datawriter, str);
ascii_size, offset, FALSE);
gst_byte_writer_put_string (&writer->datawriter, ascii_str);
} else {
/* small enough to go in the offset */
memcpy ((guint8 *) & offset, str, size);
memcpy ((guint8 *) & offset, ascii_str, ascii_size);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_ASCII,
size, offset, TRUE);
ascii_size, offset, TRUE);
}
g_free (ascii_str);
}
static void
@ -1164,7 +1183,9 @@ parse_exif_ascii_tag (GstExifReader * reader, const GstExifTagMatch * tag,
{
GType tagtype;
gchar *str;
gchar *utfstr;
guint32 real_offset;
GError *error = NULL;
if (count > 4) {
guint8 *data;
@ -1192,11 +1213,30 @@ parse_exif_ascii_tag (GstExifReader * reader, const GstExifTagMatch * tag,
str = g_strndup ((gchar *) offset_as_data, count);
}
/* convert from ascii to utf8 */
if (g_utf8_validate (str, -1, NULL)) {
GST_DEBUG ("Exif string is already on utf8: %s", str);
utfstr = str;
} else {
GST_DEBUG ("Exif string isn't utf8, trying to convert from latin1: %s",
str);
utfstr = g_convert (str, count, "utf8", "latin1", NULL, NULL, &error);
g_free (str);
if (error) {
GST_WARNING ("Skipping tag %d:%s. Failed to convert ascii string "
"to utf8 : %s - %s", tag->exif_tag, tag->gst_tag, str,
error->message);
g_error_free (error);
g_free (utfstr);
return;
}
}
tagtype = gst_tag_get_type (tag->gst_tag);
if (tagtype == GST_TYPE_DATE_TIME) {
gint year = 0, month = 1, day = 1, hour = 0, minute = 0, second = 0;
if (sscanf (str, "%04d:%02d:%02d %02d:%02d:%02d", &year, &month, &day,
if (sscanf (utfstr, "%04d:%02d:%02d %02d:%02d:%02d", &year, &month, &day,
&hour, &minute, &second) > 0) {
GstDateTime *d;
@ -1208,13 +1248,13 @@ parse_exif_ascii_tag (GstExifReader * reader, const GstExifTagMatch * tag,
GST_WARNING ("Failed to parse %s into a datetime tag", str);
}
} else if (tagtype == G_TYPE_STRING) {
gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE, tag->gst_tag, str,
NULL);
gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE, tag->gst_tag,
utfstr, NULL);
} else {
GST_WARNING ("No parsing function associated to %x(%s)", tag->exif_tag,
tag->gst_tag);
}
g_free (str);
g_free (utfstr);
}
static void

View file

@ -41,155 +41,45 @@
#include <stdlib.h>
#include <string.h>
static const gchar *genres[] = {
"Blues",
"Classic Rock",
"Country",
"Dance",
"Disco",
"Funk",
"Grunge",
"Hip-Hop",
"Jazz",
"Metal",
"New Age",
"Oldies",
"Other",
"Pop",
"R&B",
"Rap",
"Reggae",
"Rock",
"Techno",
"Industrial",
"Alternative",
"Ska",
"Death Metal",
"Pranks",
"Soundtrack",
"Euro-Techno",
"Ambient",
"Trip-Hop",
"Vocal",
"Jazz+Funk",
"Fusion",
"Trance",
"Classical",
"Instrumental",
"Acid",
"House",
"Game",
"Sound Clip",
"Gospel",
"Noise",
"Alternative Rock",
"Bass",
"Soul",
"Punk",
"Space",
"Meditative",
"Instrumental Pop",
"Instrumental Rock",
"Ethnic",
"Gothic",
"Darkwave",
"Techno-Industrial",
"Electronic",
"Pop-Folk",
"Eurodance",
"Dream",
"Southern Rock",
"Comedy",
"Cult",
"Gangsta",
"Top 40",
"Christian Rap",
"Pop/Funk",
"Jungle",
"Native American",
"Cabaret",
"New Wave",
"Psychedelic",
"Rave",
"Showtunes",
"Trailer",
"Lo-Fi",
"Tribal",
"Acid Punk",
"Acid Jazz",
"Polka",
"Retro",
"Musical",
"Rock & Roll",
"Hard Rock",
"Folk",
"Folk/Rock",
"National Folk",
"Swing",
"Fusion",
"Bebob",
"Latin",
"Revival",
"Celtic",
"Bluegrass",
"Avantgarde",
"Gothic Rock",
"Progressive Rock",
"Psychedelic Rock",
"Symphonic Rock",
"Slow Rock",
"Big Band",
"Chorus",
"Easy Listening",
"Acoustic",
"Humour",
"Speech",
"Chanson",
"Opera",
"Chamber Music",
"Sonata",
"Symphony",
"Booty Bass",
"Primus",
"Porn Groove",
"Satire",
"Slow Jam",
"Club",
"Tango",
"Samba",
"Folklore",
"Ballad",
"Power Ballad",
"Rhythmic Soul",
"Freestyle",
"Duet",
"Punk Rock",
"Drum Solo",
"A Capella",
"Euro-House",
"Dance Hall",
"Goa",
"Drum & Bass",
"Club-House",
"Hardcore",
"Terror",
"Indie",
"BritPop",
"Negerpunk",
"Polsk Punk",
"Beat",
"Christian Gangsta Rap",
"Heavy Metal",
"Black Metal",
"Crossover",
"Contemporary Christian",
"Christian Rock",
"Merengue",
"Salsa",
"Thrash Metal",
"Anime",
"Jpop",
"Synthpop"
static const gchar genres[] =
"Blues\000Classic Rock\000Country\000Dance\000Disco\000Funk\000Grunge\000"
"Hip-Hop\000Jazz\000Metal\000New Age\000Oldies\000Other\000Pop\000R&B\000"
"Rap\000Reggae\000Rock\000Techno\000Industrial\000Alternative\000Ska\000"
"Death Metal\000Pranks\000Soundtrack\000Euro-Techno\000Ambient\000Trip-Hop"
"\000Vocal\000Jazz+Funk\000Fusion\000Trance\000Classical\000Instrumental\000"
"Acid\000House\000Game\000Sound Clip\000Gospel\000Noise\000Alternative Rock"
"\000Bass\000Soul\000Punk\000Space\000Meditative\000Instrumental Pop\000"
"Instrumental Rock\000Ethnic\000Gothic\000Darkwave\000Techno-Industrial\000"
"Electronic\000Pop-Folk\000Eurodance\000Dream\000Southern Rock\000Comedy"
"\000Cult\000Gangsta\000Top 40\000Christian Rap\000Pop/Funk\000Jungle\000"
"Native American\000Cabaret\000New Wave\000Psychedelic\000Rave\000Showtunes"
"\000Trailer\000Lo-Fi\000Tribal\000Acid Punk\000Acid Jazz\000Polka\000"
"Retro\000Musical\000Rock & Roll\000Hard Rock\000Folk\000Folk/Rock\000"
"National Folk\000Swing\000Bebob\000Latin\000Revival\000Celtic\000Bluegrass"
"\000Avantgarde\000Gothic Rock\000Progressive Rock\000Psychedelic Rock\000"
"Symphonic Rock\000Slow Rock\000Big Band\000Chorus\000Easy Listening\000"
"Acoustic\000Humour\000Speech\000Chanson\000Opera\000Chamber Music\000"
"Sonata\000Symphony\000Booty Bass\000Primus\000Porn Groove\000Satire\000"
"Slow Jam\000Club\000Tango\000Samba\000Folklore\000Ballad\000Power Ballad\000"
"Rhythmic Soul\000Freestyle\000Duet\000Punk Rock\000Drum Solo\000A Capella"
"\000Euro-House\000Dance Hall\000Goa\000Drum & Bass\000Club-House\000"
"Hardcore\000Terror\000Indie\000BritPop\000Negerpunk\000Polsk Punk\000"
"Beat\000Christian Gangsta Rap\000Heavy Metal\000Black Metal\000"
"Crossover\000Contemporary Christian\000Christian Rock\000Merengue\000"
"Salsa\000Thrash Metal\000Anime\000Jpop\000Synthpop";
static const guint16 genres_idx[] = {
0, 6, 19, 27, 33, 39, 44, 51, 59, 64, 70, 78, 85, 91, 95, 99, 103, 110, 115,
122, 133, 145, 149, 161, 168, 179, 191, 199, 208, 214, 224, 231, 238, 248,
261, 266, 272, 277, 288, 295, 301, 318, 323, 328, 333, 339, 350, 367, 385,
392, 399, 408, 426, 437, 446, 456, 462, 476, 483, 488, 496, 503, 517, 526,
533, 549, 557, 566, 578, 583, 593, 601, 607, 614, 624, 634, 640, 646, 654,
666, 676, 681, 691, 705, 224, 711, 717, 723, 731, 738, 748, 759, 771, 788,
805, 820, 830, 839, 846, 861, 870, 877, 884, 892, 898, 912, 919, 928, 939,
946, 958, 965, 974, 979, 985, 991, 1000, 1007, 1020, 1034, 1044, 1049,
1059, 1069, 1079, 1090, 1101, 1105, 1117, 1128, 1137, 1144, 1150, 1158,
1168, 1179, 1184, 1206, 1218, 1230, 1240, 1263, 1278, 1287, 1293, 1306,
1312, 1317
};
static const GstTagEntryMatch tag_matches[] = {
@ -412,7 +302,7 @@ gst_tag_list_new_from_id3v1 (const guint8 * data)
guint
gst_tag_id3_genre_count (void)
{
return G_N_ELEMENTS (genres);
return G_N_ELEMENTS (genres_idx);
}
/**
@ -426,9 +316,13 @@ gst_tag_id3_genre_count (void)
const gchar *
gst_tag_id3_genre_get (const guint id)
{
if (id >= G_N_ELEMENTS (genres))
guint idx;
if (id >= G_N_ELEMENTS (genres_idx))
return NULL;
return genres[id];
idx = genres_idx[id];
g_assert (idx < sizeof (genres));
return &genres[idx];
}
/**

View file

@ -0,0 +1,559 @@
/* GStreamer tag muxer base class
* Copyright (C) 2006 Christophe Fergeau <teuf@gnome.org>
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* Copyright (C) 2006 Sebastian Dröge <slomo@circular-chaos.org>
* Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com>
*
* 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.
*/
/**
* SECTION:gsttagmux
* @see_also: GstApeMux, GstId3Mux
* @short_description: Base class for adding tags that are in one single chunk
* directly at the beginning or at the end of a file
*
* <refsect2>
* <para>
* Provides a base class for adding tags at the beginning or end of a
* stream.
* </para>
* <title>Deriving from GstTagMux</title>
* <para>
* Subclasses have to do the following things:
* <itemizedlist>
* <listitem><para>
* In their base init function, they must add pad templates for the sink
* pad and the source pad to the element class, describing the media type
* they accept and output in the caps of the pad template.
* </para></listitem>
* <listitem><para>
* In their class init function, they must override the
* GST_TAG_MUX_CLASS(mux_klass)->render_start_tag and/or
* GST_TAG_MUX_CLASS(mux_klass)->render_end_tag vfuncs and set up a render
* function.
* </para></listitem>
* </itemizedlist>
* </para>
* </refsect2>
*
* Since 0.10.36
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <gst/gsttagsetter.h>
#include <gst/tag/tag.h>
#include "gsttagmux.h"
struct _GstTagMuxPrivate
{
GstPad *srcpad;
GstPad *sinkpad;
GstTagList *event_tags; /* tags received from upstream elements */
GstTagList *final_tags; /* Final set of tags used for muxing */
gsize start_tag_size;
gsize end_tag_size;
gboolean render_start_tag;
gboolean render_end_tag;
gint64 current_offset;
gint64 max_offset;
GstEvent *newsegment_ev; /* cached newsegment event from upstream */
};
GST_DEBUG_CATEGORY_STATIC (gst_tag_mux_debug);
#define GST_CAT_DEFAULT gst_tag_mux_debug
static void
gst_tag_mux_iface_init (GType tag_type)
{
static const GInterfaceInfo tag_setter_info = {
NULL,
NULL,
NULL
};
g_type_add_interface_static (tag_type, GST_TYPE_TAG_SETTER, &tag_setter_info);
}
GST_BOILERPLATE_FULL (GstTagMux, gst_tag_mux,
GstElement, GST_TYPE_ELEMENT, gst_tag_mux_iface_init);
static GstStateChangeReturn
gst_tag_mux_change_state (GstElement * element, GstStateChange transition);
static GstFlowReturn gst_tag_mux_chain (GstPad * pad, GstBuffer * buffer);
static gboolean gst_tag_mux_sink_event (GstPad * pad, GstEvent * event);
static void
gst_tag_mux_finalize (GObject * obj)
{
GstTagMux *mux = GST_TAG_MUX (obj);
if (mux->priv->newsegment_ev) {
gst_event_unref (mux->priv->newsegment_ev);
mux->priv->newsegment_ev = NULL;
}
if (mux->priv->event_tags) {
gst_tag_list_free (mux->priv->event_tags);
mux->priv->event_tags = NULL;
}
if (mux->priv->final_tags) {
gst_tag_list_free (mux->priv->final_tags);
mux->priv->final_tags = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (obj);
}
static void
gst_tag_mux_base_init (gpointer g_class)
{
GST_DEBUG_CATEGORY_INIT (gst_tag_mux_debug, "tagmux", 0,
"tag muxer base class");
}
static void
gst_tag_mux_class_init (GstTagMuxClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_tag_mux_finalize);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_tag_mux_change_state);
g_type_class_add_private (klass, sizeof (GstTagMuxPrivate));
}
static void
gst_tag_mux_init (GstTagMux * mux, GstTagMuxClass * mux_class)
{
GstElementClass *element_klass = GST_ELEMENT_CLASS (mux_class);
GstPadTemplate *tmpl;
mux->priv =
G_TYPE_INSTANCE_GET_PRIVATE (mux, GST_TYPE_TAG_MUX, GstTagMuxPrivate);
/* pad through which data comes in to the element */
tmpl = gst_element_class_get_pad_template (element_klass, "sink");
if (tmpl) {
mux->priv->sinkpad = gst_pad_new_from_template (tmpl, "sink");
} else {
g_warning ("GstTagMux subclass '%s' did not install a %s pad template!\n",
G_OBJECT_CLASS_NAME (mux_class), "sink");
mux->priv->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
}
gst_pad_set_chain_function (mux->priv->sinkpad,
GST_DEBUG_FUNCPTR (gst_tag_mux_chain));
gst_pad_set_event_function (mux->priv->sinkpad,
GST_DEBUG_FUNCPTR (gst_tag_mux_sink_event));
gst_element_add_pad (GST_ELEMENT (mux), mux->priv->sinkpad);
/* pad through which data goes out of the element */
tmpl = gst_element_class_get_pad_template (element_klass, "src");
if (tmpl) {
GstCaps *tmpl_caps = gst_pad_template_get_caps (tmpl);
mux->priv->srcpad = gst_pad_new_from_template (tmpl, "src");
gst_pad_use_fixed_caps (mux->priv->srcpad);
if (tmpl_caps != NULL && gst_caps_is_fixed (tmpl_caps)) {
gst_pad_set_caps (mux->priv->srcpad, tmpl_caps);
}
} else {
g_warning ("GstTagMux subclass '%s' did not install a %s pad template!\n",
G_OBJECT_CLASS_NAME (mux_class), "source");
mux->priv->srcpad = gst_pad_new ("src", GST_PAD_SRC);
}
gst_element_add_pad (GST_ELEMENT (mux), mux->priv->srcpad);
mux->priv->render_start_tag = TRUE;
mux->priv->render_end_tag = TRUE;
}
static GstTagList *
gst_tag_mux_get_tags (GstTagMux * mux)
{
GstTagSetter *tagsetter = GST_TAG_SETTER (mux);
const GstTagList *tagsetter_tags;
GstTagMergeMode merge_mode;
if (mux->priv->final_tags)
return mux->priv->final_tags;
tagsetter_tags = gst_tag_setter_get_tag_list (tagsetter);
merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter);
GST_LOG_OBJECT (mux, "merging tags, merge mode = %d", merge_mode);
GST_LOG_OBJECT (mux, "event tags: %" GST_PTR_FORMAT, mux->priv->event_tags);
GST_LOG_OBJECT (mux, "set tags: %" GST_PTR_FORMAT, tagsetter_tags);
mux->priv->final_tags =
gst_tag_list_merge (tagsetter_tags, mux->priv->event_tags, merge_mode);
GST_LOG_OBJECT (mux, "final tags: %" GST_PTR_FORMAT, mux->priv->final_tags);
return mux->priv->final_tags;
}
static GstFlowReturn
gst_tag_mux_render_start_tag (GstTagMux * mux)
{
GstTagMuxClass *klass;
GstBuffer *buffer;
GstTagList *taglist;
GstEvent *event;
GstFlowReturn ret;
taglist = gst_tag_mux_get_tags (mux);
klass = GST_TAG_MUX_CLASS (G_OBJECT_GET_CLASS (mux));
if (klass->render_start_tag == NULL)
goto no_vfunc;
buffer = klass->render_start_tag (mux, taglist);
/* Null buffer is ok, just means we're not outputting anything */
if (buffer == NULL) {
GST_INFO_OBJECT (mux, "No start tag generated");
mux->priv->start_tag_size = 0;
return GST_FLOW_OK;
}
if (GST_BUFFER_CAPS (buffer) == NULL) {
buffer = gst_buffer_make_metadata_writable (buffer);
gst_buffer_set_caps (buffer, GST_PAD_CAPS (mux->priv->srcpad));
}
mux->priv->start_tag_size = GST_BUFFER_SIZE (buffer);
GST_LOG_OBJECT (mux, "tag size = %" G_GSIZE_FORMAT " bytes",
mux->priv->start_tag_size);
/* Send newsegment event from byte position 0, so the tag really gets
* written to the start of the file, independent of the upstream segment */
gst_pad_push_event (mux->priv->srcpad,
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0));
/* Send an event about the new tags to downstream elements */
/* gst_event_new_tag takes ownership of the list, so use a copy */
event = gst_event_new_tag (gst_tag_list_copy (taglist));
gst_pad_push_event (mux->priv->srcpad, event);
GST_BUFFER_OFFSET (buffer) = 0;
ret = gst_pad_push (mux->priv->srcpad, buffer);
mux->priv->current_offset = mux->priv->start_tag_size;
mux->priv->max_offset =
MAX (mux->priv->max_offset, mux->priv->current_offset);
return ret;
no_vfunc:
{
GST_ERROR_OBJECT (mux, "Subclass does not implement "
"render_start_tag vfunc!");
return GST_FLOW_ERROR;
}
}
static GstFlowReturn
gst_tag_mux_render_end_tag (GstTagMux * mux)
{
GstTagMuxClass *klass;
GstBuffer *buffer;
GstTagList *taglist;
GstFlowReturn ret;
taglist = gst_tag_mux_get_tags (mux);
klass = GST_TAG_MUX_CLASS (G_OBJECT_GET_CLASS (mux));
if (klass->render_end_tag == NULL)
goto no_vfunc;
buffer = klass->render_end_tag (mux, taglist);
if (buffer == NULL) {
GST_INFO_OBJECT (mux, "No end tag generated");
mux->priv->end_tag_size = 0;
return GST_FLOW_OK;
}
if (GST_BUFFER_CAPS (buffer) == NULL) {
buffer = gst_buffer_make_metadata_writable (buffer);
gst_buffer_set_caps (buffer, GST_PAD_CAPS (mux->priv->srcpad));
}
mux->priv->end_tag_size = GST_BUFFER_SIZE (buffer);
GST_LOG_OBJECT (mux, "tag size = %" G_GSIZE_FORMAT " bytes",
mux->priv->end_tag_size);
/* Send newsegment event from the end of the file, so it gets written there,
independent of whatever new segment events upstream has sent us */
gst_pad_push_event (mux->priv->srcpad,
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
mux->priv->max_offset, -1, 0));
GST_BUFFER_OFFSET (buffer) = mux->priv->max_offset;
ret = gst_pad_push (mux->priv->srcpad, buffer);
return ret;
no_vfunc:
{
GST_ERROR_OBJECT (mux, "Subclass does not implement "
"render_end_tag vfunc!");
return GST_FLOW_ERROR;
}
}
static GstEvent *
gst_tag_mux_adjust_event_offsets (GstTagMux * mux,
const GstEvent * newsegment_event)
{
GstFormat format;
gint64 start, stop, cur;
gst_event_parse_new_segment ((GstEvent *) newsegment_event, NULL, NULL,
&format, &start, &stop, &cur);
g_assert (format == GST_FORMAT_BYTES);
if (start != -1)
start += mux->priv->start_tag_size;
if (stop != -1)
stop += mux->priv->start_tag_size;
if (cur != -1)
cur += mux->priv->start_tag_size;
GST_DEBUG_OBJECT (mux, "adjusting newsegment event offsets to start=%"
G_GINT64_FORMAT ", stop=%" G_GINT64_FORMAT ", cur=%" G_GINT64_FORMAT
" (delta = +%" G_GSIZE_FORMAT ")", start, stop, cur,
mux->priv->start_tag_size);
return gst_event_new_new_segment (TRUE, 1.0, format, start, stop, cur);
}
static GstFlowReturn
gst_tag_mux_chain (GstPad * pad, GstBuffer * buffer)
{
GstTagMux *mux = GST_TAG_MUX (GST_OBJECT_PARENT (pad));
GstFlowReturn ret;
int length;
if (mux->priv->render_start_tag) {
GST_INFO_OBJECT (mux, "Adding tags to stream");
ret = gst_tag_mux_render_start_tag (mux);
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (mux, "flow: %s", gst_flow_get_name (ret));
gst_buffer_unref (buffer);
return ret;
}
/* Now send the cached newsegment event that we got from upstream */
if (mux->priv->newsegment_ev) {
gint64 start;
GstEvent *newseg;
GST_DEBUG_OBJECT (mux, "sending cached newsegment event");
newseg = gst_tag_mux_adjust_event_offsets (mux, mux->priv->newsegment_ev);
gst_event_unref (mux->priv->newsegment_ev);
mux->priv->newsegment_ev = NULL;
gst_event_parse_new_segment (newseg, NULL, NULL, NULL, &start, NULL,
NULL);
gst_pad_push_event (mux->priv->srcpad, newseg);
mux->priv->current_offset = start;
mux->priv->max_offset =
MAX (mux->priv->max_offset, mux->priv->current_offset);
} else {
/* upstream sent no newsegment event or only one in a non-BYTE format */
}
mux->priv->render_start_tag = FALSE;
}
buffer = gst_buffer_make_metadata_writable (buffer);
if (GST_BUFFER_OFFSET (buffer) != GST_BUFFER_OFFSET_NONE) {
GST_LOG_OBJECT (mux, "Adjusting buffer offset from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, GST_BUFFER_OFFSET (buffer),
GST_BUFFER_OFFSET (buffer) + mux->priv->start_tag_size);
GST_BUFFER_OFFSET (buffer) += mux->priv->start_tag_size;
}
length = GST_BUFFER_SIZE (buffer);
gst_buffer_set_caps (buffer, GST_PAD_CAPS (mux->priv->srcpad));
ret = gst_pad_push (mux->priv->srcpad, buffer);
mux->priv->current_offset += length;
mux->priv->max_offset =
MAX (mux->priv->max_offset, mux->priv->current_offset);
return ret;
}
static gboolean
gst_tag_mux_sink_event (GstPad * pad, GstEvent * event)
{
GstTagMux *mux;
gboolean result;
mux = GST_TAG_MUX (gst_pad_get_parent (pad));
result = FALSE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_TAG:{
GstTagList *tags;
gst_event_parse_tag (event, &tags);
GST_INFO_OBJECT (mux, "Got tag event: %" GST_PTR_FORMAT, tags);
if (mux->priv->event_tags != NULL) {
gst_tag_list_insert (mux->priv->event_tags, tags,
GST_TAG_MERGE_REPLACE);
} else {
mux->priv->event_tags = gst_tag_list_copy (tags);
}
GST_INFO_OBJECT (mux, "Event tags are now: %" GST_PTR_FORMAT,
mux->priv->event_tags);
/* just drop the event, we'll push a new tag event in render_start_tag */
gst_event_unref (event);
result = TRUE;
break;
}
case GST_EVENT_NEWSEGMENT:{
GstFormat fmt;
gint64 start;
gst_event_parse_new_segment (event, NULL, NULL, &fmt, &start, NULL, NULL);
if (fmt != GST_FORMAT_BYTES) {
GST_WARNING_OBJECT (mux, "dropping newsegment event in %s format",
gst_format_get_name (fmt));
gst_event_unref (event);
break;
}
if (mux->priv->render_start_tag) {
/* we have not rendered the tag yet, which means that we don't know
* how large it is going to be yet, so we can't adjust the offsets
* here at this point and need to cache the newsegment event for now
* (also, there could be tag events coming after this newsegment event
* and before the first buffer). */
if (mux->priv->newsegment_ev) {
GST_WARNING_OBJECT (mux, "discarding old cached newsegment event");
gst_event_unref (mux->priv->newsegment_ev);
}
GST_LOG_OBJECT (mux, "caching newsegment event for later");
mux->priv->newsegment_ev = event;
} else {
GST_DEBUG_OBJECT (mux, "got newsegment event, adjusting offsets");
gst_pad_push_event (mux->priv->srcpad,
gst_tag_mux_adjust_event_offsets (mux, event));
gst_event_unref (event);
mux->priv->current_offset = start;
mux->priv->max_offset =
MAX (mux->priv->max_offset, mux->priv->current_offset);
}
event = NULL;
result = TRUE;
break;
}
case GST_EVENT_EOS:{
if (mux->priv->render_end_tag) {
GstFlowReturn ret;
GST_INFO_OBJECT (mux, "Adding tags to stream");
ret = gst_tag_mux_render_end_tag (mux);
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (mux, "flow: %s", gst_flow_get_name (ret));
return ret;
}
mux->priv->render_end_tag = FALSE;
}
/* Now forward EOS */
result = gst_pad_event_default (pad, event);
break;
}
default:
result = gst_pad_event_default (pad, event);
break;
}
gst_object_unref (mux);
return result;
}
static GstStateChangeReturn
gst_tag_mux_change_state (GstElement * element, GstStateChange transition)
{
GstTagMux *mux;
GstStateChangeReturn result;
mux = GST_TAG_MUX (element);
result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (result != GST_STATE_CHANGE_SUCCESS) {
return result;
}
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:{
if (mux->priv->newsegment_ev) {
gst_event_unref (mux->priv->newsegment_ev);
mux->priv->newsegment_ev = NULL;
}
if (mux->priv->event_tags) {
gst_tag_list_free (mux->priv->event_tags);
mux->priv->event_tags = NULL;
}
mux->priv->start_tag_size = 0;
mux->priv->end_tag_size = 0;
mux->priv->render_start_tag = TRUE;
mux->priv->render_end_tag = TRUE;
mux->priv->current_offset = 0;
mux->priv->max_offset = 0;
break;
}
default:
break;
}
return result;
}

View file

@ -0,0 +1,89 @@
/* GStreamer tag muxer base class
* Copyright (C) 2006 Christophe Fergeau <teuf@gnome.org>
* Copyright (C) 2006,2011 Tim-Philipp Müller <tim centricular net>
* Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com>
*
* 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_TAG_MUX_H
#define GST_TAG_MUX_H
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_TAG_MUX \
(gst_tag_mux_get_type())
#define GST_TAG_MUX(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAG_MUX,GstTagMux))
#define GST_TAG_MUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAG_MUX,GstTagMuxClass))
#define GST_IS_TAG_MUX(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAG_MUX))
#define GST_IS_TAG_MUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAG_MUX))
typedef struct _GstTagMux GstTagMux;
typedef struct _GstTagMuxClass GstTagMuxClass;
typedef struct _GstTagMuxPrivate GstTagMuxPrivate;
/**
* GstTagMux:
* @element: parent element
*
* Opaque #GstTagMux structure.
*
* Since: 0.10.36
*/
struct _GstTagMux {
GstElement element;
/*< private >*/
GstTagMuxPrivate *priv;
gpointer _gst_reserved[GST_PADDING];
};
/**
* GstTagMuxClass:
* @parent_class: the parent class.
* @render_start_tag: create a tag buffer to add to the beginning of the
* input stream given a tag list, or NULL
* @render_end_tag: create a tag buffer to add to the end of the
* input stream given a tag list, or NULL
*
* The #GstTagMuxClass structure. Subclasses need to override at least one
* of the two render vfuncs.
*
* Since: 0.10.36
*/
struct _GstTagMuxClass {
GstElementClass parent_class;
/* vfuncs */
GstBuffer * (*render_start_tag) (GstTagMux * mux, const GstTagList * tag_list);
GstBuffer * (*render_end_tag) (GstTagMux * mux, const GstTagList * tag_list);
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
GType gst_tag_mux_get_type (void);
G_END_DECLS
#endif

View file

@ -98,6 +98,11 @@ static const GstTagEntryMatch tag_matches[] = {
* http://mail.kde.org/pipermail/amarok/2006-May/000090.html
*/
{GST_TAG_BEATS_PER_MINUTE, "BPM"},
/* What GStreamer calls encoder ("encoder used to encode this stream") is
stored in the vendor string in Vorbis/Theora/Kate and possibly others.
The Vorbis comment packet used in those streams uses ENCODER as the name
of the encoding program, which GStreamer calls application-name. */
{GST_TAG_APPLICATION_NAME, "ENCODER"},
{NULL, NULL}
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,733 @@
Informal standard M. Nilsson
Document: id3v2.4.0-structure.txt 16 September 2001
ID3 tag version 2.4.0 - Main Structure
Status of this document
This document is an informal standard and replaces the ID3v2.3.0
standard [ID3v2]. A formal standard will use another revision number
even if the content is identical to document. The contents in this
document may change for clarifications but never for added or altered
functionallity.
Distribution of this document is unlimited.
Abstract
This document describes the main structure of ID3v2.4.0, which is a
revised version of the ID3v2 informal standard [ID3v2] version
2.3.0. The ID3v2 offers a flexible way of storing audio meta
information within the audio file itself. The information may be
technical information, such as equalisation curves, as well as
title, performer, copyright etc.
ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order
to allow for implementations to be revised as easily as possible.
1. Table of contents
Status of this document
Abstract
1. Table of contents
2. Conventions in this document
2. Standard overview
3. ID3v2 overview
3.1. ID3v2 header
3.2. ID3v2 extended header
3.3. Padding
3.4. ID3v2 footer
4. ID3v2 frames overview
4.1. Frame header flags
4.1.1. Frame status flags
4.1.2. Frame format flags
5. Tag location
6. Unsynchronisation
6.1. The unsynchronisation scheme
6.2. Synchsafe integers
7. Copyright
8. References
9. Author's Address
2. Conventions in this document
Text within "" is a text string exactly as it appears in a tag.
Numbers preceded with $ are hexadecimal and numbers preceded with %
are binary. $xx is used to indicate a byte with unknown content. %x
is used to indicate a bit with unknown content. The most significant
bit (MSB) of a byte is called 'bit 7' and the least significant bit
(LSB) is called 'bit 0'.
A tag is the whole tag described in this document. A frame is a block
of information in the tag. The tag consists of a header, frames and
optional padding. A field is a piece of information; one value, a
string etc. A numeric string is a string that consists of the
characters "0123456789" only.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in RFC 2119 [KEYWORDS].
3. ID3v2 overview
ID3v2 is a general tagging format for audio, which makes it possible
to store meta data about the audio inside the audio file itself. The
ID3 tag described in this document is mainly targeted at files
encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III
and MPEG-2.5, but may work with other types of encoded audio or as a
stand alone format for audio meta data.
ID3v2 is designed to be as flexible and expandable as possible to
meet new meta information needs that might arise. To achieve that
ID3v2 is constructed as a container for several information blocks,
called frames, whose format need not be known to the software that
encounters them. At the start of every frame is an unique and
predefined identifier, a size descriptor that allows software to skip
unknown frames and a flags field. The flags describes encoding
details and if the frame should remain in the tag, should it be
unknown to the software, if the file is altered.
The bitorder in ID3v2 is most significant bit first (MSB). The
byteorder in multibyte numbers is most significant byte first (e.g.
$12345678 would be encoded $12 34 56 78), also known as big endian
and network byte order.
Overall tag structure:
+-----------------------------+
| Header (10 bytes) |
+-----------------------------+
| Extended Header |
| (variable length, OPTIONAL) |
+-----------------------------+
| Frames (variable length) |
+-----------------------------+
| Padding |
| (variable length, OPTIONAL) |
+-----------------------------+
| Footer (10 bytes, OPTIONAL) |
+-----------------------------+
In general, padding and footer are mutually exclusive. See details in
sections 3.3, 3.4 and 5.
3.1. ID3v2 header
The first part of the ID3v2 tag is the 10 byte tag header, laid out
as follows:
ID3v2/file identifier "ID3"
ID3v2 version $04 00
ID3v2 flags %abcd0000
ID3v2 size 4 * %0xxxxxxx
The first three bytes of the tag are always "ID3", to indicate that
this is an ID3v2 tag, directly followed by the two version bytes. The
first byte of ID3v2 version is its major version, while the second
byte is its revision number. In this case this is ID3v2.4.0. All
revisions are backwards compatible while major versions are not. If
software with ID3v2.4.0 and below support should encounter version
five or higher it should simply ignore the whole tag. Version or
revision will never be $FF.
The version is followed by the ID3v2 flags field, of which currently
four flags are used.
a - Unsynchronisation
Bit 7 in the 'ID3v2 flags' indicates whether or not
unsynchronisation is applied on all frames (see section 6.1 for
details); a set bit indicates usage.
b - Extended header
The second bit (bit 6) indicates whether or not the header is
followed by an extended header. The extended header is described in
section 3.2. A set bit indicates the presence of an extended
header.
c - Experimental indicator
The third bit (bit 5) is used as an 'experimental indicator'. This
flag SHALL always be set when the tag is in an experimental stage.
d - Footer present
Bit 4 indicates that a footer (section 3.4) is present at the very
end of the tag. A set bit indicates the presence of a footer.
All the other flags MUST be cleared. If one of these undefined flags
are set, the tag might not be readable for a parser that does not
know the flags function.
The ID3v2 tag size is stored as a 32 bit synchsafe integer (section
6.2), making a total of 28 effective bits (representing up to 256MB).
The ID3v2 tag size is the sum of the byte length of the extended
header, the padding and the frames after unsynchronisation. If a
footer is present this equals to ('total size' - 20) bytes, otherwise
('total size' - 10) bytes.
An ID3v2 tag can be detected with the following pattern:
$49 44 33 yy yy xx zz zz zz zz
Where yy is less than $FF, xx is the 'flags' byte and zz is less than
$80.
3.2. Extended header
The extended header contains information that can provide further
insight in the structure of the tag, but is not vital to the correct
parsing of the tag information; hence the extended header is
optional.
Extended header size 4 * %0xxxxxxx
Number of flag bytes $01
Extended Flags $xx
Where the 'Extended header size' is the size of the whole extended
header, stored as a 32 bit synchsafe integer. An extended header can
thus never have a size of fewer than six bytes.
The extended flags field, with its size described by 'number of flag
bytes', is defined as:
%0bcd0000
Each flag that is set in the extended header has data attached, which
comes in the order in which the flags are encountered (i.e. the data
for flag 'b' comes before the data for flag 'c'). Unset flags cannot
have any attached data. All unknown flags MUST be unset and their
corresponding data removed when a tag is modified.
Every set flag's data starts with a length byte, which contains a
value between 0 and 127 ($00 - $7f), followed by data that has the
field length indicated by the length byte. If a flag has no attached
data, the value $00 is used as length byte.
b - Tag is an update
If this flag is set, the present tag is an update of a tag found
earlier in the present file or stream. If frames defined as unique
are found in the present tag, they are to override any
corresponding ones found in the earlier tag. This flag has no
corresponding data.
Flag data length $00
c - CRC data present
If this flag is set, a CRC-32 [ISO-3309] data is included in the
extended header. The CRC is calculated on all the data between the
header and footer as indicated by the header's tag length field,
minus the extended header. Note that this includes the padding (if
there is any), but excludes the footer. The CRC-32 is stored as an
35 bit synchsafe integer, leaving the upper four bits always
zeroed.
Flag data length $05
Total frame CRC 5 * %0xxxxxxx
d - Tag restrictions
For some applications it might be desired to restrict a tag in more
ways than imposed by the ID3v2 specification. Note that the
presence of these restrictions does not affect how the tag is
decoded, merely how it was restricted before encoding. If this flag
is set the tag is restricted as follows:
Flag data length $01
Restrictions %ppqrrstt
p - Tag size restrictions
00 No more than 128 frames and 1 MB total tag size.
01 No more than 64 frames and 128 KB total tag size.
10 No more than 32 frames and 40 KB total tag size.
11 No more than 32 frames and 4 KB total tag size.
q - Text encoding restrictions
0 No restrictions
1 Strings are only encoded with ISO-8859-1 [ISO-8859-1] or
UTF-8 [UTF-8].
r - Text fields size restrictions
00 No restrictions
01 No string is longer than 1024 characters.
10 No string is longer than 128 characters.
11 No string is longer than 30 characters.
Note that nothing is said about how many bytes is used to
represent those characters, since it is encoding dependent. If a
text frame consists of more than one string, the sum of the
strungs is restricted as stated.
s - Image encoding restrictions
0 No restrictions
1 Images are encoded only with PNG [PNG] or JPEG [JFIF].
t - Image size restrictions
00 No restrictions
01 All images are 256x256 pixels or smaller.
10 All images are 64x64 pixels or smaller.
11 All images are exactly 64x64 pixels, unless required
otherwise.
3.3. Padding
It is OPTIONAL to include padding after the final frame (at the end
of the ID3 tag), making the size of all the frames together smaller
than the size given in the tag header. A possible purpose of this
padding is to allow for adding a few additional frames or enlarge
existing frames within the tag without having to rewrite the entire
file. The value of the padding bytes must be $00. A tag MUST NOT have
any padding between the frames or between the tag header and the
frames. Furthermore it MUST NOT have any padding when a tag footer is
added to the tag.
3.4. ID3v2 footer
To speed up the process of locating an ID3v2 tag when searching from
the end of a file, a footer can be added to the tag. It is REQUIRED
to add a footer to an appended tag, i.e. a tag located after all
audio data. The footer is a copy of the header, but with a different
identifier.
ID3v2 identifier "3DI"
ID3v2 version $04 00
ID3v2 flags %abcd0000
ID3v2 size 4 * %0xxxxxxx
4. ID3v2 frame overview
All ID3v2 frames consists of one frame header followed by one or more
fields containing the actual information. The header is always 10
bytes and laid out as follows:
Frame ID $xx xx xx xx (four characters)
Size 4 * %0xxxxxxx
Flags $xx xx
The frame ID is made out of the characters capital A-Z and 0-9.
Identifiers beginning with "X", "Y" and "Z" are for experimental
frames and free for everyone to use, without the need to set the
experimental bit in the tag header. Bear in mind that someone else
might have used the same identifier as you. All other identifiers are
either used or reserved for future use.
The frame ID is followed by a size descriptor containing the size of
the data in the final frame, after encryption, compression and
unsynchronisation. The size is excluding the frame header ('total
frame size' - 10 bytes) and stored as a 32 bit synchsafe integer.
In the frame header the size descriptor is followed by two flag
bytes. These flags are described in section 4.1.
There is no fixed order of the frames' appearance in the tag,
although it is desired that the frames are arranged in order of
significance concerning the recognition of the file. An example of
such order: UFID, TIT2, MCDI, TRCK ...
A tag MUST contain at least one frame. A frame must be at least 1
byte big, excluding the header.
If nothing else is said, strings, including numeric strings and URLs
[URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the
range $20 - $FF. Such strings are represented in frame descriptions
as <text string>, or <full text string> if newlines are allowed. If
nothing else is said newline character is forbidden. In ISO-8859-1 a
newline is represented, when allowed, with $0A only.
Frames that allow different types of text encoding contains a text
encoding description byte. Possible encodings:
$00 ISO-8859-1 [ISO-8859-1]. Terminated with $00.
$01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All
strings in the same frame SHALL have the same byteorder.
Terminated with $00 00.
$02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
Terminated with $00 00.
$03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.
Strings dependent on encoding are represented in frame descriptions
as <text string according to encoding>, or <full text string
according to encoding> if newlines are allowed. Any empty strings of
type $01 which are NULL-terminated may have the Unicode BOM followed
by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00).
The timestamp fields are based on a subset of ISO 8601. When being as
precise as possible the format of a time string is
yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of
24), ":", minutes, ":", seconds), but the precision may be reduced by
removing as many time indicators as wanted. Hence valid timestamps
are
yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and
yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use
the slash character as described in 8601, and for multiple non-
contiguous dates, use multiple strings, if allowed by the frame
definition.
The three byte language field, present in several frames, is used to
describe the language of the frame's content, according to ISO-639-2
[ISO-639-2]. The language should be represented in lower case. If the
language is not known the string "XXX" should be used.
All URLs [URL] MAY be relative, e.g. "picture.png", "../doc.txt".
If a frame is longer than it should be, e.g. having more fields than
specified in this document, that indicates that additions to the
frame have been made in a later version of the ID3v2 standard. This
is reflected by the revision number in the header of the tag.
4.1. Frame header flags
In the frame header the size descriptor is followed by two flag
bytes. All unused flags MUST be cleared. The first byte is for
'status messages' and the second byte is a format description. If an
unknown flag is set in the first byte the frame MUST NOT be changed
without that bit cleared. If an unknown flag is set in the second
byte the frame is likely to not be readable. Some flags in the second
byte indicates that extra information is added to the header. These
fields of extra information is ordered as the flags that indicates
them. The flags field is defined as follows (l and o left out because
ther resemblence to one and zero):
%0abc0000 %0h00kmnp
Some frame format flags indicate that additional information fields
are added to the frame. This information is added after the frame
header and before the frame data in the same order as the flags that
indicates them. I.e. the four bytes of decompressed size will precede
the encryption method byte. These additions affects the 'frame size'
field, but are not subject to encryption or compression.
The default status flags setting for a frame is, unless stated
otherwise, 'preserved if tag is altered' and 'preserved if file is
altered', i.e. %00000000.
4.1.1. Frame status flags
a - Tag alter preservation
This flag tells the tag parser what to do with this frame if it is
unknown and the tag is altered in any way. This applies to all
kinds of alterations, including adding more padding and reordering
the frames.
0 Frame should be preserved.
1 Frame should be discarded.
b - File alter preservation
This flag tells the tag parser what to do with this frame if it is
unknown and the file, excluding the tag, is altered. This does not
apply when the audio is completely replaced with other audio data.
0 Frame should be preserved.
1 Frame should be discarded.
c - Read only
This flag, if set, tells the software that the contents of this
frame are intended to be read only. Changing the contents might
break something, e.g. a signature. If the contents are changed,
without knowledge of why the frame was flagged read only and
without taking the proper means to compensate, e.g. recalculating
the signature, the bit MUST be cleared.
4.1.2. Frame format flags
h - Grouping identity
This flag indicates whether or not this frame belongs in a group
with other frames. If set, a group identifier byte is added to the
frame. Every frame with the same group identifier belongs to the
same group.
0 Frame does not contain group information
1 Frame contains group information
k - Compression
This flag indicates whether or not the frame is compressed.
A 'Data Length Indicator' byte MUST be included in the frame.
0 Frame is not compressed.
1 Frame is compressed using zlib [zlib] deflate method.
If set, this requires the 'Data Length Indicator' bit
to be set as well.
m - Encryption
This flag indicates whether or not the frame is encrypted. If set,
one byte indicating with which method it was encrypted will be
added to the frame. See description of the ENCR frame for more
information about encryption method registration. Encryption
should be done after compression. Whether or not setting this flag
requires the presence of a 'Data Length Indicator' depends on the
specific algorithm used.
0 Frame is not encrypted.
1 Frame is encrypted.
n - Unsynchronisation
This flag indicates whether or not unsynchronisation was applied
to this frame. See section 6 for details on unsynchronisation.
If this flag is set all data from the end of this header to the
end of this frame has been unsynchronised. Although desirable, the
presence of a 'Data Length Indicator' is not made mandatory by
unsynchronisation.
0 Frame has not been unsynchronised.
1 Frame has been unsyrchronised.
p - Data length indicator
This flag indicates that a data length indicator has been added to
the frame. The data length indicator is the value one would write
as the 'Frame length' if all of the frame format flags were
zeroed, represented as a 32 bit synchsafe integer.
0 There is no Data Length Indicator.
1 A data length Indicator has been added to the frame.
5. Tag location
The default location of an ID3v2 tag is prepended to the audio so
that players can benefit from the information when the data is
streamed. It is however possible to append the tag, or make a
prepend/append combination. When deciding upon where an unembedded
tag should be located, the following order of preference SHOULD be
considered.
1. Prepend the tag.
2. Prepend a tag with all vital information and add a second tag at
the end of the file, before tags from other tagging systems. The
first tag is required to have a SEEK frame.
3. Add a tag at the end of the file, before tags from other tagging
systems.
In case 2 and 3 the tag can simply be appended if no other known tags
are present. The suggested method to find ID3v2 tags are:
1. Look for a prepended tag using the pattern found in section 3.1.
2. If a SEEK frame was found, use its values to guide further
searching.
3. Look for a tag footer, scanning from the back of the file.
For every new tag that is found, the old tag should be discarded
unless the update flag in the extended header (section 3.2) is set.
6. Unsynchronisation
The only purpose of unsynchronisation is to make the ID3v2 tag as
compatible as possible with existing software and hardware. There is
no use in 'unsynchronising' tags if the file is only to be processed
only by ID3v2 aware software and hardware. Unsynchronisation is only
useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC
files.
6.1. The unsynchronisation scheme
Whenever a false synchronisation is found within the tag, one zeroed
byte is inserted after the first false synchronisation byte. The
format of synchronisations that should be altered by ID3 encoders is
as follows:
%11111111 111xxxxx
and should be replaced with:
%11111111 00000000 111xxxxx
This has the side effect that all $FF 00 combinations have to be
altered, so they will not be affected by the decoding process.
Therefore all the $FF 00 combinations have to be replaced with the
$FF 00 00 combination during the unsynchronisation.
To indicate usage of the unsynchronisation, the unsynchronisation
flag in the frame header should be set. This bit MUST be set if the
frame was altered by the unsynchronisation and SHOULD NOT be set if
unaltered. If all frames in the tag are unsynchronised the
unsynchronisation flag in the tag header SHOULD be set. It MUST NOT
be set if the tag has a frame which is not unsynchronised.
Assume the first byte of the audio to be $FF. The special case when
the last byte of the last frame is $FF and no padding nor footer is
used will then introduce a false synchronisation. This can be solved
by adding a footer, adding padding or unsynchronising the frame and
add $00 to the end of the frame data, thus adding more byte to the
frame size than a normal unsynchronisation would. Although not
preferred, it is allowed to apply the last method on all frames
ending with $FF.
It is preferred that the tag is either completely unsynchronised or
not unsynchronised at all. A completely unsynchronised tag has no
false synchonisations in it, as defined above, and does not end with
$FF. A completely non-unsynchronised tag contains no unsynchronised
frames, and thus the unsynchronisation flag in the header is cleared.
Do bear in mind, that if compression or encryption is used, the
unsynchronisation scheme MUST be applied afterwards. When decoding an
unsynchronised frame, the unsynchronisation scheme MUST be reversed
first, encryption and decompression afterwards.
6.2. Synchsafe integers
In some parts of the tag it is inconvenient to use the
unsychronisation scheme because the size of unsynchronised data is
not known in advance, which is particularly problematic with size
descriptors. The solution in ID3v2 is to use synchsafe integers, in
which there can never be any false synchs. Synchsafe integers are
integers that keep its highest bit (bit 7) zeroed, making seven bits
out of eight available. Thus a 32 bit synchsafe integer can store 28
bits of information.
Example:
255 (%11111111) encoded as a 16 bit synchsafe integer is 383
(%00000001 01111111).
7. Copyright
Copyright (C) Martin Nilsson 2000. All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that a reference to this document is included on all
such copies and derivative works. However, this document itself may
not be modified in any way and reissued as the original document.
The limited permissions granted above are perpetual and will not be
revoked.
This document and the information contained herein is provided on an
'AS IS' basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
8. References
[ID3v2] Martin Nilsson, 'ID3v2 informal standard'.
<url:http://www.id3.org/id3v2.3.0.txt>
[ISO-639-2] ISO/FDIS 639-2.
'Codes for the representation of names of languages, Part 2: Alpha-3
code.' Technical committee / subcommittee: TC 37 / SC 2
[ISO-3309] ISO 3309
'Information Processing Systems--Data Communication High-Level Data
Link Control Procedure--Frame Structure', IS 3309, October 1984, 3rd
Edition.
[ISO-8859-1] ISO/IEC DIS 8859-1.
'8-bit single-byte coded graphic character sets, Part 1: Latin
alphabet No. 1.' Technical committee / subcommittee: JTC 1 / SC 2
[JFIF] 'JPEG File Interchange Format, version 1.02'
<url:http://www.w3.org/Graphics/JPEG/jfif.txt>
[KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate
Requirement Levels', RFC 2119, March 1997.
<url:ftp://ftp.isi.edu/in-notes/rfc2119.txt>
[MPEG] ISO/IEC 11172-3:1993.
'Coding of moving pictures and associated audio for digital storage
media at up to about 1,5 Mbit/s, Part 3: Audio.'
Technical committee / subcommittee: JTC 1 / SC 29
and
ISO/IEC 13818-3:1995
'Generic coding of moving pictures and associated audio information,
Part 3: Audio.'
Technical committee / subcommittee: JTC 1 / SC 29
and
ISO/IEC DIS 13818-3
'Generic coding of moving pictures and associated audio information,
Part 3: Audio (Revision of ISO/IEC 13818-3:1995)'
[PNG] 'Portable Network Graphics, version 1.0'
<url:http://www.w3.org/TR/REC-png-multi.html>
[UNICODE] The Unicode Consortium,
'The Unicode Standard Version 3.0', ISBN 0-201-61633-5.
<url:http://www.unicode.org/unicode/standard/versions/Unicode3.0.htm>
[URL] T. Berners-Lee, L. Masinter & M. McCahill, 'Uniform Resource
Locators (URL)', RFC 1738, December 1994.
<url:ftp://ftp.isi.edu/in-notes/rfc1738.txt>
[UTF-8] F. Yergeau, 'UTF-8, a transformation format of ISO 10646',
RFC 2279, January 1998.
<url:ftp://ftp.isi.edu/in-notes/rfc2279.txt>
[UTF-16] F. Yergeau, 'UTF-16, an encoding of ISO 10646', RFC 2781,
February 2000.
<url:ftp://ftp.isi.edu/in-notes/rfc2781.txt>
[ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, 'ZLIB
Compressed Data Format Specification version 3.3', RFC 1950,
May 1996.
<url:ftp://ftp.isi.edu/in-notes/rfc1950.txt>
9. Author's Address
Written by
Martin Nilsson
Rydsvägen 246 C. 30
SE-584 34 Linköping
Sweden
Email: nilsson@id3.org

582
gst-libs/gst/tag/id3v2.c Normal file
View file

@ -0,0 +1,582 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
* Copyright 2002,2003 Scott Wheeler <wheeler@kde.org> (portions from taglib)
*
* 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 <gst/tag/tag.h>
#include "id3v2.h"
#define HANDLE_INVALID_SYNCSAFE
static gboolean id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size);
#ifndef GST_DISABLE_GST_DEBUG
#define GST_CAT_DEFAULT id3v2_ensure_debug_category()
GstDebugCategory *
id3v2_ensure_debug_category (void)
{
static gsize cat_gonce = 0;
if (g_once_init_enter (&cat_gonce)) {
gsize cat;
cat = (gsize) _gst_debug_category_new ("id3v2", 0, "ID3v2 tag parsing");
g_once_init_leave (&cat_gonce, cat);
}
return (GstDebugCategory *) cat_gonce;
}
#endif /* GST_DISABLE_GST_DEBUG */
guint
id3v2_read_synch_uint (const guint8 * data, guint size)
{
gint i;
guint result = 0;
gint invalid = 0;
g_assert (size <= 4);
size--;
for (i = 0; i <= size; i++) {
invalid |= data[i] & 0x80;
result |= (data[i] & 0x7f) << ((size - i) * 7);
}
#ifdef HANDLE_INVALID_SYNCSAFE
if (invalid) {
GST_WARNING ("Invalid synch-safe integer in ID3v2 frame "
"- using the actual value instead");
result = 0;
for (i = 0; i <= size; i++) {
result |= data[i] << ((size - i) * 8);
}
}
#endif
return result;
}
/**
* gst_tag_get_id3v2_tag_size:
* @buffer: buffer holding ID3v2 tag (or at least the start of one)
*
* Determines size of an ID3v2 tag on buffer containing at least ID3v2 header,
* i.e. at least #GST_TAG_ID3V2_HEADER_SIZE (10) bytes;
*
* Returns: Size of tag, or 0 if header is invalid or too small.
*
* Since: 0.10.36
*/
guint
gst_tag_get_id3v2_tag_size (GstBuffer * buffer)
{
guint8 *data, flags;
guint size;
g_return_val_if_fail (buffer != NULL, 0);
if (GST_BUFFER_SIZE (buffer) < ID3V2_HDR_SIZE)
return 0;
data = GST_BUFFER_DATA (buffer);
/* Check for 'ID3' string at start of buffer */
if (data[0] != 'I' || data[1] != 'D' || data[2] != '3') {
GST_DEBUG ("No ID3v2 tag in data");
return 0;
}
/* Read the flags */
flags = data[5];
/* Read the size from the header */
size = id3v2_read_synch_uint (data + 6, 4);
if (size == 0)
return ID3V2_HDR_SIZE;
size += ID3V2_HDR_SIZE;
/* Expand the read size to include a footer if there is one */
if ((flags & ID3V2_HDR_FLAG_FOOTER))
size += 10;
GST_DEBUG ("ID3v2 tag, size: %u bytes", size);
return size;
}
guint8 *
id3v2_ununsync_data (const guint8 * unsync_data, guint32 * size)
{
const guint8 *end;
guint8 *out, *uu;
guint out_size;
uu = out = g_malloc (*size);
for (end = unsync_data + *size; unsync_data < end - 1; ++unsync_data, ++uu) {
*uu = *unsync_data;
if (G_UNLIKELY (*unsync_data == 0xff && *(unsync_data + 1) == 0x00))
++unsync_data;
}
/* take care of last byte (if last two bytes weren't 0xff 0x00) */
if (unsync_data < end) {
*uu = *unsync_data;
++uu;
}
out_size = uu - out;
GST_DEBUG ("size after un-unsyncing: %u (before: %u)", out_size, *size);
*size = out_size;
return out;
}
/**
* gst_tag_list_from_id3v2_tag:
* @buffer: buffer to convert
*
* Creates a new tag list that contains the information parsed out of a
* ID3 tag.
*
* Returns: A new #GstTagList with all tags that could be extracted from the
* given vorbiscomment buffer or NULL on error.
*
* Since: 0.10.36
*/
GstTagList *
gst_tag_list_from_id3v2_tag (GstBuffer * buffer)
{
guint8 *data, *uu_data = NULL;
guint read_size;
ID3TagsWorking work;
guint8 flags;
guint16 version;
read_size = gst_tag_get_id3v2_tag_size (buffer);
/* Ignore tag if it has no frames attached, but skip the header then */
if (read_size < ID3V2_HDR_SIZE)
return NULL;
data = GST_BUFFER_DATA (buffer);
/* Read the version */
version = GST_READ_UINT16_BE (data + 3);
/* Read the flags */
flags = data[5];
/* Validate the version. At the moment, we only support up to 2.4.0 */
if (ID3V2_VER_MAJOR (version) > 4 || ID3V2_VER_MINOR (version) > 0) {
GST_WARNING ("ID3v2 tag is from revision 2.%d.%d, "
"but decoder only supports 2.%d.%d. Ignoring as per spec.",
version >> 8, version & 0xff, ID3V2_VERSION >> 8, ID3V2_VERSION & 0xff);
return NULL;
}
GST_DEBUG ("ID3v2 header flags: %s %s %s %s",
(flags & ID3V2_HDR_FLAG_UNSYNC) ? "UNSYNC" : "",
(flags & ID3V2_HDR_FLAG_EXTHDR) ? "EXTENDED_HEADER" : "",
(flags & ID3V2_HDR_FLAG_EXPERIMENTAL) ? "EXPERIMENTAL" : "",
(flags & ID3V2_HDR_FLAG_FOOTER) ? "FOOTER" : "");
/* This shouldn't really happen! Caller should have checked first */
if (GST_BUFFER_SIZE (buffer) < read_size) {
GST_DEBUG
("Found ID3v2 tag with revision 2.%d.%d - need %u more bytes to read",
version >> 8, version & 0xff,
(guint) (read_size - GST_BUFFER_SIZE (buffer)));
return NULL;
}
GST_DEBUG ("Reading ID3v2 tag with revision 2.%d.%d of size %u", version >> 8,
version & 0xff, read_size);
GST_MEMDUMP ("ID3v2 tag", GST_BUFFER_DATA (buffer), read_size);
memset (&work, 0, sizeof (ID3TagsWorking));
work.buffer = buffer;
work.hdr.version = version;
work.hdr.size = read_size;
work.hdr.flags = flags;
work.hdr.frame_data = GST_BUFFER_DATA (buffer) + ID3V2_HDR_SIZE;
if (flags & ID3V2_HDR_FLAG_FOOTER)
work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE - 10;
else
work.hdr.frame_data_size = read_size - ID3V2_HDR_SIZE;
/* in v2.3 the frame sizes are not syncsafe, so the entire tag had to be
* unsynced. In v2.4 the frame sizes are syncsafe so it's just the frame
* data that needs un-unsyncing, but not the frame headers. */
if ((flags & ID3V2_HDR_FLAG_UNSYNC) != 0 && ID3V2_VER_MAJOR (version) <= 3) {
GST_DEBUG ("Un-unsyncing entire tag");
uu_data = id3v2_ununsync_data (work.hdr.frame_data,
&work.hdr.frame_data_size);
work.hdr.frame_data = uu_data;
GST_MEMDUMP ("ID3v2 tag (un-unsyced)", uu_data, work.hdr.frame_data_size);
}
id3v2_frames_to_tag_list (&work, work.hdr.frame_data_size);
g_free (uu_data);
return work.tags;
}
static guint
id3v2_frame_hdr_size (guint id3v2ver)
{
/* ID3v2 < 2.3.0 only had 6 byte header */
switch (ID3V2_VER_MAJOR (id3v2ver)) {
case 0:
case 1:
case 2:
return 6;
case 3:
case 4:
default:
return 10;
}
}
static const gchar obsolete_frame_ids[][5] = {
{"CRM"}, {"EQU"}, {"LNK"}, {"RVA"}, {"TIM"}, {"TSI"}, /* From 2.2 */
{"EQUA"}, {"RVAD"}, {"TIME"}, {"TRDA"}, {"TSIZ"} /* From 2.3 */
};
static const struct ID3v2FrameIDConvert
{
const gchar orig[5];
const gchar new[5];
} frame_id_conversions[] = {
/* 2.3.x frames */
{
"TORY", "TDOR"}, {
"TYER", "TDRC"},
/* 2.2.x frames */
{
"BUF", "RBUF"}, {
"CNT", "PCNT"}, {
"COM", "COMM"}, {
"CRA", "AENC"}, {
"ETC", "ETCO"}, {
"GEO", "GEOB"}, {
"IPL", "TIPL"}, {
"MCI", "MCDI"}, {
"MLL", "MLLT"}, {
"PIC", "APIC"}, {
"POP", "POPM"}, {
"REV", "RVRB"}, {
"SLT", "SYLT"}, {
"STC", "SYTC"}, {
"TAL", "TALB"}, {
"TBP", "TBPM"}, {
"TCM", "TCOM"}, {
"TCO", "TCON"}, {
"TCR", "TCOP"}, {
"TDA", "TDAT"}, { /* obsolete, but we need to parse it anyway */
"TDY", "TDLY"}, {
"TEN", "TENC"}, {
"TFT", "TFLT"}, {
"TKE", "TKEY"}, {
"TLA", "TLAN"}, {
"TLE", "TLEN"}, {
"TMT", "TMED"}, {
"TOA", "TOAL"}, {
"TOF", "TOFN"}, {
"TOL", "TOLY"}, {
"TOR", "TDOR"}, {
"TOT", "TOAL"}, {
"TP1", "TPE1"}, {
"TP2", "TPE2"}, {
"TP3", "TPE3"}, {
"TP4", "TPE4"}, {
"TPA", "TPOS"}, {
"TPB", "TPUB"}, {
"TRC", "TSRC"}, {
"TRD", "TDRC"}, {
"TRK", "TRCK"}, {
"TSS", "TSSE"}, {
"TT1", "TIT1"}, {
"TT2", "TIT2"}, {
"TT3", "TIT3"}, {
"TXT", "TOLY"}, {
"TXX", "TXXX"}, {
"TYE", "TDRC"}, {
"UFI", "UFID"}, {
"ULT", "USLT"}, {
"WAF", "WOAF"}, {
"WAR", "WOAR"}, {
"WAS", "WOAS"}, {
"WCM", "WCOM"}, {
"WCP", "WCOP"}, {
"WPB", "WPUB"}, {
"WXX", "WXXX"}
};
static gboolean
convert_fid_to_v240 (gchar * frame_id)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (obsolete_frame_ids); ++i) {
if (strncmp (frame_id, obsolete_frame_ids[i], 5) == 0)
return TRUE;
}
for (i = 0; i < G_N_ELEMENTS (frame_id_conversions); ++i) {
if (strncmp (frame_id, frame_id_conversions[i].orig, 5) == 0) {
strcpy (frame_id, frame_id_conversions[i].new);
return FALSE;
}
}
return FALSE;
}
/* add unknown or unhandled ID3v2 frames to the taglist as binary blobs */
static void
id3v2_add_id3v2_frame_blob_to_taglist (ID3TagsWorking * work, guint size)
{
GstBuffer *blob;
GstCaps *caps;
guint8 *frame_data;
gchar *media_type;
guint frame_size, header_size;
guint i;
switch (ID3V2_VER_MAJOR (work->hdr.version)) {
case 1:
case 2:
header_size = 3 + 3;
break;
case 3:
case 4:
header_size = 4 + 4 + 2;
break;
default:
g_return_if_reached ();
}
frame_data = work->hdr.frame_data - header_size;
frame_size = size + header_size;
blob = gst_buffer_new_and_alloc (frame_size);
memcpy (GST_BUFFER_DATA (blob), frame_data, frame_size);
/* Sanitize frame id */
for (i = 0; i < 4; i++) {
if (!g_ascii_isalnum (frame_data[i]))
frame_data[i] = '_';
}
media_type = g_strdup_printf ("application/x-gst-id3v2-%c%c%c%c-frame",
g_ascii_tolower (frame_data[0]), g_ascii_tolower (frame_data[1]),
g_ascii_tolower (frame_data[2]), g_ascii_tolower (frame_data[3]));
caps = gst_caps_new_simple (media_type, "version", G_TYPE_INT,
(gint) ID3V2_VER_MAJOR (work->hdr.version), NULL);
gst_buffer_set_caps (blob, caps);
gst_caps_unref (caps);
g_free (media_type);
/* gst_util_dump_mem (GST_BUFFER_DATA (blob), GST_BUFFER_SIZE (blob)); */
gst_tag_list_add (work->tags, GST_TAG_MERGE_APPEND,
GST_TAG_ID3V2_FRAME, blob, NULL);
gst_buffer_unref (blob);
}
static gboolean
id3v2_frames_to_tag_list (ID3TagsWorking * work, guint size)
{
guint frame_hdr_size;
guint8 *start;
/* Extended header if present */
if (work->hdr.flags & ID3V2_HDR_FLAG_EXTHDR) {
work->hdr.ext_hdr_size = id3v2_read_synch_uint (work->hdr.frame_data, 4);
if (work->hdr.ext_hdr_size < 6 ||
(work->hdr.ext_hdr_size) > work->hdr.frame_data_size) {
GST_DEBUG ("Invalid extended header. Broken tag");
return FALSE;
}
work->hdr.ext_flag_bytes = work->hdr.frame_data[4];
if (5 + work->hdr.ext_flag_bytes > work->hdr.frame_data_size) {
GST_DEBUG
("Tag claims extended header, but doesn't have enough bytes. Broken tag");
return FALSE;
}
work->hdr.ext_flag_data = work->hdr.frame_data + 5;
work->hdr.frame_data += work->hdr.ext_hdr_size;
work->hdr.frame_data_size -= work->hdr.ext_hdr_size;
}
start = GST_BUFFER_DATA (work->buffer);
frame_hdr_size = id3v2_frame_hdr_size (work->hdr.version);
if (work->hdr.frame_data_size <= frame_hdr_size) {
GST_DEBUG ("Tag has no data frames. Broken tag");
return FALSE; /* Must have at least one frame */
}
work->tags = gst_tag_list_new ();
while (work->hdr.frame_data_size > frame_hdr_size) {
guint frame_size = 0;
gchar frame_id[5] = "";
guint16 frame_flags = 0x0;
gboolean obsolete_id = FALSE;
gboolean read_synch_size = TRUE;
guint i;
/* Read the header */
switch (ID3V2_VER_MAJOR (work->hdr.version)) {
case 0:
case 1:
case 2:
frame_id[0] = work->hdr.frame_data[0];
frame_id[1] = work->hdr.frame_data[1];
frame_id[2] = work->hdr.frame_data[2];
frame_id[3] = 0;
frame_id[4] = 0;
obsolete_id = convert_fid_to_v240 (frame_id);
/* 3 byte non-synchsafe size */
frame_size = work->hdr.frame_data[3] << 16 |
work->hdr.frame_data[4] << 8 | work->hdr.frame_data[5];
frame_flags = 0;
break;
case 3:
read_synch_size = FALSE; /* 2.3 frame size is not synch-safe */
case 4:
default:
frame_id[0] = work->hdr.frame_data[0];
frame_id[1] = work->hdr.frame_data[1];
frame_id[2] = work->hdr.frame_data[2];
frame_id[3] = work->hdr.frame_data[3];
frame_id[4] = 0;
if (read_synch_size)
frame_size = id3v2_read_synch_uint (work->hdr.frame_data + 4, 4);
else
frame_size = GST_READ_UINT32_BE (work->hdr.frame_data + 4);
frame_flags = GST_READ_UINT16_BE (work->hdr.frame_data + 8);
if (ID3V2_VER_MAJOR (work->hdr.version) == 3) {
frame_flags &= ID3V2_3_FRAME_FLAGS_MASK;
obsolete_id = convert_fid_to_v240 (frame_id);
if (obsolete_id)
GST_DEBUG ("Ignoring v2.3 frame %s", frame_id);
}
break;
}
work->hdr.frame_data += frame_hdr_size;
work->hdr.frame_data_size -= frame_hdr_size;
if (frame_size > work->hdr.frame_data_size || strcmp (frame_id, "") == 0)
break; /* No more frames to read */
/* Sanitize frame id */
switch (ID3V2_VER_MAJOR (work->hdr.version)) {
case 0:
case 1:
case 2:
for (i = 0; i < 3; i++) {
if (!g_ascii_isalnum (frame_id[i]))
frame_id[i] = '_';
}
break;
default:
for (i = 0; i < 4; i++) {
if (!g_ascii_isalnum (frame_id[i]))
frame_id[i] = '_';
}
}
#if 1
GST_LOG
("Frame @ %ld (0x%02lx) id %s size %u, next=%ld (0x%02lx) obsolete=%d",
(glong) (work->hdr.frame_data - start),
(glong) (work->hdr.frame_data - start), frame_id, frame_size,
(glong) (work->hdr.frame_data + frame_hdr_size + frame_size - start),
(glong) (work->hdr.frame_data + frame_hdr_size + frame_size - start),
obsolete_id);
#define flag_string(flag,str) \
((frame_flags & (flag)) ? (str) : "")
GST_LOG ("Frame header flags: 0x%04x %s %s %s %s %s %s %s", frame_flags,
flag_string (ID3V2_FRAME_STATUS_FRAME_ALTER_PRESERVE, "ALTER_PRESERVE"),
flag_string (ID3V2_FRAME_STATUS_READONLY, "READONLY"),
flag_string (ID3V2_FRAME_FORMAT_GROUPING_ID, "GROUPING_ID"),
flag_string (ID3V2_FRAME_FORMAT_COMPRESSION, "COMPRESSION"),
flag_string (ID3V2_FRAME_FORMAT_ENCRYPTION, "ENCRYPTION"),
flag_string (ID3V2_FRAME_FORMAT_UNSYNCHRONISATION, "UNSYNC"),
flag_string (ID3V2_FRAME_FORMAT_DATA_LENGTH_INDICATOR, "LENGTH_IND"));
#undef flag_str
#endif
if (!obsolete_id) {
/* Now, read, decompress etc the contents of the frame
* into a TagList entry */
work->cur_frame_size = frame_size;
work->frame_id = frame_id;
work->frame_flags = frame_flags;
if (id3v2_parse_frame (work)) {
GST_LOG ("Extracted frame with id %s", frame_id);
} else {
GST_LOG ("Failed to extract frame with id %s", frame_id);
id3v2_add_id3v2_frame_blob_to_taglist (work, frame_size);
}
}
work->hdr.frame_data += frame_size;
work->hdr.frame_data_size -= frame_size;
}
if (gst_structure_n_fields (GST_STRUCTURE (work->tags)) == 0) {
GST_DEBUG ("Could not extract any frames from tag. Broken or empty tag");
gst_tag_list_free (work->tags);
work->tags = NULL;
return FALSE;
}
/* Set day/month now if they were in a separate (obsolete) TDAT frame */
if (work->pending_day != 0 && work->pending_month != 0) {
GDate *date = NULL;
if (gst_tag_list_get_date (work->tags, GST_TAG_DATE, &date)) {
g_date_set_day (date, work->pending_day);
g_date_set_month (date, work->pending_month);
gst_tag_list_add (work->tags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE,
date, NULL);
g_date_free (date);
}
}
return TRUE;
}

120
gst-libs/gst/tag/id3v2.h Normal file
View file

@ -0,0 +1,120 @@
/* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
*
* 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 __ID3TAGS_H__
#define __ID3TAGS_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define ID3V2_MARK_SIZE 3
#define ID3V2_HDR_SIZE GST_TAG_ID3V2_HEADER_SIZE
/* From id3v2.c */
guint id3v2_read_synch_uint (const guint8 * data, guint size);
/* Things shared by id3tags.c and id3v2frames.c */
#define ID3V2_VERSION 0x0400
#define ID3V2_VER_MAJOR(v) ((v) >> 8)
#define ID3V2_VER_MINOR(v) ((v) & 0xff)
typedef struct {
guint16 version;
guint8 flags;
guint32 size;
guint8 *frame_data;
guint32 frame_data_size;
guint32 ext_hdr_size;
guint8 ext_flag_bytes;
guint8 *ext_flag_data;
} ID3v2Header;
typedef struct {
ID3v2Header hdr;
GstBuffer *buffer;
GstTagList *tags;
/* Current frame decoding */
guint cur_frame_size;
gchar *frame_id;
guint16 frame_flags;
guint8 *parse_data;
guint parse_size;
/* To collect day/month from obsolete TDAT frame if it exists */
guint pending_month;
guint pending_day;
} ID3TagsWorking;
enum {
ID3V2_HDR_FLAG_UNSYNC = 0x80,
ID3V2_HDR_FLAG_EXTHDR = 0x40,
ID3V2_HDR_FLAG_EXPERIMENTAL = 0x20,
ID3V2_HDR_FLAG_FOOTER = 0x10
};
enum {
ID3V2_EXT_FLAG_UPDATE = 0x80,
ID3V2_EXT_FLAG_CRC = 0x40,
ID3V2_EXT_FLAG_RESTRICTED = 0x20
};
enum {
ID3V2_FRAME_STATUS_FRAME_ALTER_PRESERVE = 0x4000,
ID3V2_FRAME_STATUS_FILE_ALTER_PRESERVE = 0x2000,
ID3V2_FRAME_STATUS_READONLY = 0x1000,
ID3V2_FRAME_FORMAT_GROUPING_ID = 0x0040,
ID3V2_FRAME_FORMAT_COMPRESSION = 0x0008,
ID3V2_FRAME_FORMAT_ENCRYPTION = 0x0004,
ID3V2_FRAME_FORMAT_UNSYNCHRONISATION = 0x0002,
ID3V2_FRAME_FORMAT_DATA_LENGTH_INDICATOR = 0x0001
};
#define ID3V2_3_FRAME_FLAGS_MASK \
(ID3V2_FRAME_STATUS_FRAME_ALTER_PRESERVE | \
ID3V2_FRAME_STATUS_FILE_ALTER_PRESERVE | \
ID3V2_FRAME_STATUS_READONLY | \
ID3V2_FRAME_FORMAT_GROUPING_ID | \
ID3V2_FRAME_FORMAT_COMPRESSION | \
ID3V2_FRAME_FORMAT_ENCRYPTION)
/* FIXME 0.11: remove 'private' bit from GST_TAG_ID3V2_FRAME */
/**
* GST_TAG_ID3V2_FRAME:
*
* Contains a single unprocessed ID3v2 frame. (buffer)
*
* (Not public API for now)
*/
#define GST_TAG_ID3V2_FRAME "private-id3v2-frame"
/* From id3v2frames.c */
gboolean id3v2_parse_frame (ID3TagsWorking *work);
guint8 * id3v2_ununsync_data (const guint8 * unsync_data, guint32 * size);
GstDebugCategory * id3v2_ensure_debug_category (void);
G_END_DECLS
#endif

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -0,0 +1,467 @@
/* created by mklicensestables.c */
static const struct {
/* jurisdictions in addition to the generic version, bitfield */
const guint64 jurisdictions;
const GstTagLicenseFlags flags;
/* the bit after http://creativecommons.org/licenses/ */
const gchar ref[18];
gint16 title_idx; /* index in string table */
gint16 desc_idx; /* index in string table */
} licenses[] = {
/* 0 http://creativecommons.org/licenses/GPL/2.0/ */
{ 0x8000000000000000, 0x02000d07, "GPL/2.0/", 0, -1 },
/* 1 http://creativecommons.org/licenses/LGPL/2.1/ */
{ 0x8000000000000000, 0x02000d07, "LGPL/2.1/", 4, -1 },
/* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/at/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/au/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/be/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/br/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/ca/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/cl/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/de/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/es/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/fr/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/hr/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/it/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/kr/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/nl/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/pl/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/tw/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/uk/
* 2 http://creativecommons.org/licenses/by-nc-nd/2.0/za/
* 2 http://creativecommons.org/licenses/by-nd-nc/2.0/jp/ */
{ 0x8000002c2871a96e, 0x01010303, "by-nc-nd/2.0/", 9, -1 },
/* 3 http://creativecommons.org/licenses/by-nc-nd/2.1/au/ */
{ 0x0000000000000004, 0x01010303, "by-nc-nd/2.1/", 9, -1 },
/* 4 http://creativecommons.org/licenses/by-nc-nd/2.1/es/ */
{ 0x0000000000002000, 0x01010303, "by-nc-nd/2.1/", 9, -1 },
/* 5 http://creativecommons.org/licenses/by-nc-nd/2.1/jp/ */
{ 0x0000000000200000, 0x01010303, "by-nc-nd/2.1/", 9, -1 },
/* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/ar/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/au/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/bg/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/br/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/ca/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/ch/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/cn/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/co/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/dk/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/es/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/hr/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/hu/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/il/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/in/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/it/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/mk/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/mt/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/mx/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/my/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/nl/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/pe/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/pl/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/pt/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/scotland/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/se/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/si/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/tw/
* 6 http://creativecommons.org/licenses/by-nc-nd/2.5/za/ */
{ 0x80000027ff9f36f5, 0x01010303, "by-nc-nd/2.5/", 9, -1 },
/* 7 http://creativecommons.org/licenses/by-nc-nd/3.0/
* 7 http://creativecommons.org/licenses/by-nc-nd/3.0/us/ */
{ 0x8000001000000000, 0x01010303, "by-nc-nd/3.0/", 9, -1 },
/* 8 http://creativecommons.org/licenses/by-nc-sa/1.0/
* 8 http://creativecommons.org/licenses/by-nc-sa/1.0/fi/
* 8 http://creativecommons.org/licenses/by-nc-sa/1.0/il/
* 8 http://creativecommons.org/licenses/by-nc-sa/1.0/nl/ */
{ 0x8000000008044000, 0x01010707, "by-nc-sa/1.0/", 44, -1 },
/* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/at/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/au/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/be/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/br/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/ca/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/cl/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/de/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/es/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/fr/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/hr/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/it/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/jp/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/kr/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/nl/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/pl/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/tw/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/uk/
* 9 http://creativecommons.org/licenses/by-nc-sa/2.0/za/ */
{ 0x8000002c2871a96e, 0x01010707, "by-nc-sa/2.0/", 44, -1 },
/* 10 http://creativecommons.org/licenses/by-nc-sa/2.1/au/ */
{ 0x0000000000000004, 0x01010707, "by-nc-sa/2.1/", 44, -1 },
/* 11 http://creativecommons.org/licenses/by-nc-sa/2.1/es/ */
{ 0x0000000000002000, 0x01010707, "by-nc-sa/2.1/", 44, -1 },
/* 12 http://creativecommons.org/licenses/by-nc-sa/2.1/jp/ */
{ 0x0000000000200000, 0x01010707, "by-nc-sa/2.1/", 44, -1 },
/* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/ar/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/au/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/bg/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/br/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/ca/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/ch/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/cn/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/co/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/dk/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/es/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/hr/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/hu/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/il/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/in/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/it/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/mk/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/mt/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/mx/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/my/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/nl/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/pe/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/pl/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/pt/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/se/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/si/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/tw/
* 13 http://creativecommons.org/licenses/by-nc-sa/2.5/za/ */
{ 0x800000277f9f36f5, 0x01010707, "by-nc-sa/2.5/", 44, -1 },
/* 14 http://creativecommons.org/licenses/by-nc-sa/2.5/scotland/ */
{ 0x0000000080000000, 0x01010703, "by-nc-sa/2.5/", 44, -1 },
/* 15 http://creativecommons.org/licenses/by-nc-sa/3.0/
* 15 http://creativecommons.org/licenses/by-nc-sa/3.0/us/ */
{ 0x8000001000000000, 0x01010707, "by-nc-sa/3.0/", 44, -1 },
/* 16 http://creativecommons.org/licenses/by-nc/1.0/
* 16 http://creativecommons.org/licenses/by-nc/1.0/fi/
* 16 http://creativecommons.org/licenses/by-nc/1.0/il/
* 16 http://creativecommons.org/licenses/by-nc/1.0/nl/ */
{ 0x8000000008044000, 0x01010307, "by-nc/1.0/", 81, -1 },
/* 17 http://creativecommons.org/licenses/by-nc/2.0/
* 17 http://creativecommons.org/licenses/by-nc/2.0/at/
* 17 http://creativecommons.org/licenses/by-nc/2.0/au/
* 17 http://creativecommons.org/licenses/by-nc/2.0/be/
* 17 http://creativecommons.org/licenses/by-nc/2.0/br/
* 17 http://creativecommons.org/licenses/by-nc/2.0/ca/
* 17 http://creativecommons.org/licenses/by-nc/2.0/cl/
* 17 http://creativecommons.org/licenses/by-nc/2.0/de/
* 17 http://creativecommons.org/licenses/by-nc/2.0/es/
* 17 http://creativecommons.org/licenses/by-nc/2.0/fr/
* 17 http://creativecommons.org/licenses/by-nc/2.0/hr/
* 17 http://creativecommons.org/licenses/by-nc/2.0/it/
* 17 http://creativecommons.org/licenses/by-nc/2.0/jp/
* 17 http://creativecommons.org/licenses/by-nc/2.0/kr/
* 17 http://creativecommons.org/licenses/by-nc/2.0/nl/
* 17 http://creativecommons.org/licenses/by-nc/2.0/pl/
* 17 http://creativecommons.org/licenses/by-nc/2.0/tw/
* 17 http://creativecommons.org/licenses/by-nc/2.0/uk/
* 17 http://creativecommons.org/licenses/by-nc/2.0/za/ */
{ 0x8000002c2871a96e, 0x01010307, "by-nc/2.0/", 81, -1 },
/* 18 http://creativecommons.org/licenses/by-nc/2.1/au/ */
{ 0x0000000000000004, 0x01010307, "by-nc/2.1/", 81, -1 },
/* 19 http://creativecommons.org/licenses/by-nc/2.1/es/ */
{ 0x0000000000002000, 0x01010307, "by-nc/2.1/", 81, -1 },
/* 20 http://creativecommons.org/licenses/by-nc/2.1/jp/ */
{ 0x0000000000200000, 0x01010307, "by-nc/2.1/", 81, -1 },
/* 21 http://creativecommons.org/licenses/by-nc/2.5/
* 21 http://creativecommons.org/licenses/by-nc/2.5/ar/
* 21 http://creativecommons.org/licenses/by-nc/2.5/au/
* 21 http://creativecommons.org/licenses/by-nc/2.5/bg/
* 21 http://creativecommons.org/licenses/by-nc/2.5/br/
* 21 http://creativecommons.org/licenses/by-nc/2.5/ca/
* 21 http://creativecommons.org/licenses/by-nc/2.5/ch/
* 21 http://creativecommons.org/licenses/by-nc/2.5/cn/
* 21 http://creativecommons.org/licenses/by-nc/2.5/co/
* 21 http://creativecommons.org/licenses/by-nc/2.5/dk/
* 21 http://creativecommons.org/licenses/by-nc/2.5/es/
* 21 http://creativecommons.org/licenses/by-nc/2.5/hr/
* 21 http://creativecommons.org/licenses/by-nc/2.5/hu/
* 21 http://creativecommons.org/licenses/by-nc/2.5/il/
* 21 http://creativecommons.org/licenses/by-nc/2.5/in/
* 21 http://creativecommons.org/licenses/by-nc/2.5/it/
* 21 http://creativecommons.org/licenses/by-nc/2.5/mk/
* 21 http://creativecommons.org/licenses/by-nc/2.5/mt/
* 21 http://creativecommons.org/licenses/by-nc/2.5/mx/
* 21 http://creativecommons.org/licenses/by-nc/2.5/my/
* 21 http://creativecommons.org/licenses/by-nc/2.5/nl/
* 21 http://creativecommons.org/licenses/by-nc/2.5/pe/
* 21 http://creativecommons.org/licenses/by-nc/2.5/pl/
* 21 http://creativecommons.org/licenses/by-nc/2.5/pt/
* 21 http://creativecommons.org/licenses/by-nc/2.5/se/
* 21 http://creativecommons.org/licenses/by-nc/2.5/si/
* 21 http://creativecommons.org/licenses/by-nc/2.5/tw/
* 21 http://creativecommons.org/licenses/by-nc/2.5/za/ */
{ 0x800000277f9f36f5, 0x01010307, "by-nc/2.5/", 81, -1 },
/* 22 http://creativecommons.org/licenses/by-nc/2.5/scotland/ */
{ 0x0000000080000000, 0x01010303, "by-nc/2.5/", 81, -1 },
/* 23 http://creativecommons.org/licenses/by-nc/3.0/
* 23 http://creativecommons.org/licenses/by-nc/3.0/us/ */
{ 0x8000001000000000, 0x01010307, "by-nc/3.0/", 81, -1 },
/* 24 http://creativecommons.org/licenses/by-nd-nc/1.0/
* 24 http://creativecommons.org/licenses/by-nd-nc/1.0/fi/
* 24 http://creativecommons.org/licenses/by-nd-nc/1.0/il/
* 24 http://creativecommons.org/licenses/by-nd-nc/1.0/nl/ */
{ 0x8000000008044000, 0x01010303, "by-nd-nc/1.0/", 107, -1 },
/* 25 http://creativecommons.org/licenses/by-nd-nc/2.0/jp/ */
{ 0x0000000000200000, 0x01010303, "by-nd-nc/2.0/", 107, -1 },
/* 26 http://creativecommons.org/licenses/by-nd/1.0/
* 26 http://creativecommons.org/licenses/by-nd/1.0/fi/
* 26 http://creativecommons.org/licenses/by-nd/1.0/il/
* 26 http://creativecommons.org/licenses/by-nd/1.0/nl/ */
{ 0x8000000008044000, 0x01000303, "by-nd/1.0/", 142, -1 },
/* 27 http://creativecommons.org/licenses/by-nd/2.0/
* 27 http://creativecommons.org/licenses/by-nd/2.0/at/
* 27 http://creativecommons.org/licenses/by-nd/2.0/au/
* 27 http://creativecommons.org/licenses/by-nd/2.0/be/
* 27 http://creativecommons.org/licenses/by-nd/2.0/br/
* 27 http://creativecommons.org/licenses/by-nd/2.0/ca/
* 27 http://creativecommons.org/licenses/by-nd/2.0/cl/
* 27 http://creativecommons.org/licenses/by-nd/2.0/de/
* 27 http://creativecommons.org/licenses/by-nd/2.0/es/
* 27 http://creativecommons.org/licenses/by-nd/2.0/fr/
* 27 http://creativecommons.org/licenses/by-nd/2.0/hr/
* 27 http://creativecommons.org/licenses/by-nd/2.0/it/
* 27 http://creativecommons.org/licenses/by-nd/2.0/jp/
* 27 http://creativecommons.org/licenses/by-nd/2.0/kr/
* 27 http://creativecommons.org/licenses/by-nd/2.0/nl/
* 27 http://creativecommons.org/licenses/by-nd/2.0/pl/
* 27 http://creativecommons.org/licenses/by-nd/2.0/tw/
* 27 http://creativecommons.org/licenses/by-nd/2.0/uk/
* 27 http://creativecommons.org/licenses/by-nd/2.0/za/ */
{ 0x8000002c2871a96e, 0x01000303, "by-nd/2.0/", 142, -1 },
/* 28 http://creativecommons.org/licenses/by-nd/2.1/au/ */
{ 0x0000000000000004, 0x01000303, "by-nd/2.1/", 142, -1 },
/* 29 http://creativecommons.org/licenses/by-nd/2.1/es/ */
{ 0x0000000000002000, 0x01000303, "by-nd/2.1/", 142, -1 },
/* 30 http://creativecommons.org/licenses/by-nd/2.1/jp/ */
{ 0x0000000000200000, 0x01000303, "by-nd/2.1/", 142, -1 },
/* 31 http://creativecommons.org/licenses/by-nd/2.5/
* 31 http://creativecommons.org/licenses/by-nd/2.5/ar/
* 31 http://creativecommons.org/licenses/by-nd/2.5/au/
* 31 http://creativecommons.org/licenses/by-nd/2.5/bg/
* 31 http://creativecommons.org/licenses/by-nd/2.5/br/
* 31 http://creativecommons.org/licenses/by-nd/2.5/ca/
* 31 http://creativecommons.org/licenses/by-nd/2.5/ch/
* 31 http://creativecommons.org/licenses/by-nd/2.5/cn/
* 31 http://creativecommons.org/licenses/by-nd/2.5/co/
* 31 http://creativecommons.org/licenses/by-nd/2.5/dk/
* 31 http://creativecommons.org/licenses/by-nd/2.5/es/
* 31 http://creativecommons.org/licenses/by-nd/2.5/hr/
* 31 http://creativecommons.org/licenses/by-nd/2.5/hu/
* 31 http://creativecommons.org/licenses/by-nd/2.5/il/
* 31 http://creativecommons.org/licenses/by-nd/2.5/in/
* 31 http://creativecommons.org/licenses/by-nd/2.5/it/
* 31 http://creativecommons.org/licenses/by-nd/2.5/mk/
* 31 http://creativecommons.org/licenses/by-nd/2.5/mt/
* 31 http://creativecommons.org/licenses/by-nd/2.5/mx/
* 31 http://creativecommons.org/licenses/by-nd/2.5/my/
* 31 http://creativecommons.org/licenses/by-nd/2.5/nl/
* 31 http://creativecommons.org/licenses/by-nd/2.5/pe/
* 31 http://creativecommons.org/licenses/by-nd/2.5/pl/
* 31 http://creativecommons.org/licenses/by-nd/2.5/pt/
* 31 http://creativecommons.org/licenses/by-nd/2.5/scotland/
* 31 http://creativecommons.org/licenses/by-nd/2.5/se/
* 31 http://creativecommons.org/licenses/by-nd/2.5/si/
* 31 http://creativecommons.org/licenses/by-nd/2.5/tw/
* 31 http://creativecommons.org/licenses/by-nd/2.5/za/ */
{ 0x80000027ff9f36f5, 0x01000303, "by-nd/2.5/", 142, -1 },
/* 32 http://creativecommons.org/licenses/by-nd/3.0/
* 32 http://creativecommons.org/licenses/by-nd/3.0/us/ */
{ 0x8000001000000000, 0x01000303, "by-nd/3.0/", 142, -1 },
/* 33 http://creativecommons.org/licenses/by-sa/1.0/
* 33 http://creativecommons.org/licenses/by-sa/1.0/fi/
* 33 http://creativecommons.org/licenses/by-sa/1.0/il/
* 33 http://creativecommons.org/licenses/by-sa/1.0/nl/ */
{ 0x8000000008044000, 0x01000707, "by-sa/1.0/", 163, -1 },
/* 34 http://creativecommons.org/licenses/by-sa/2.0/
* 34 http://creativecommons.org/licenses/by-sa/2.0/at/
* 34 http://creativecommons.org/licenses/by-sa/2.0/au/
* 34 http://creativecommons.org/licenses/by-sa/2.0/be/
* 34 http://creativecommons.org/licenses/by-sa/2.0/br/
* 34 http://creativecommons.org/licenses/by-sa/2.0/ca/
* 34 http://creativecommons.org/licenses/by-sa/2.0/cl/
* 34 http://creativecommons.org/licenses/by-sa/2.0/de/
* 34 http://creativecommons.org/licenses/by-sa/2.0/es/
* 34 http://creativecommons.org/licenses/by-sa/2.0/fr/
* 34 http://creativecommons.org/licenses/by-sa/2.0/hr/
* 34 http://creativecommons.org/licenses/by-sa/2.0/it/
* 34 http://creativecommons.org/licenses/by-sa/2.0/jp/
* 34 http://creativecommons.org/licenses/by-sa/2.0/kr/
* 34 http://creativecommons.org/licenses/by-sa/2.0/nl/
* 34 http://creativecommons.org/licenses/by-sa/2.0/pl/
* 34 http://creativecommons.org/licenses/by-sa/2.0/tw/
* 34 http://creativecommons.org/licenses/by-sa/2.0/uk/
* 34 http://creativecommons.org/licenses/by-sa/2.0/za/ */
{ 0x8000002c2871a96e, 0x01000707, "by-sa/2.0/", 163, -1 },
/* 35 http://creativecommons.org/licenses/by-sa/2.1/au/ */
{ 0x0000000000000004, 0x01000707, "by-sa/2.1/", 163, -1 },
/* 36 http://creativecommons.org/licenses/by-sa/2.1/es/ */
{ 0x0000000000002000, 0x01000707, "by-sa/2.1/", 163, -1 },
/* 37 http://creativecommons.org/licenses/by-sa/2.1/jp/ */
{ 0x0000000000200000, 0x01000707, "by-sa/2.1/", 163, -1 },
/* 38 http://creativecommons.org/licenses/by-sa/2.5/
* 38 http://creativecommons.org/licenses/by-sa/2.5/ar/
* 38 http://creativecommons.org/licenses/by-sa/2.5/au/
* 38 http://creativecommons.org/licenses/by-sa/2.5/bg/
* 38 http://creativecommons.org/licenses/by-sa/2.5/br/
* 38 http://creativecommons.org/licenses/by-sa/2.5/ca/
* 38 http://creativecommons.org/licenses/by-sa/2.5/ch/
* 38 http://creativecommons.org/licenses/by-sa/2.5/cn/
* 38 http://creativecommons.org/licenses/by-sa/2.5/co/
* 38 http://creativecommons.org/licenses/by-sa/2.5/dk/
* 38 http://creativecommons.org/licenses/by-sa/2.5/es/
* 38 http://creativecommons.org/licenses/by-sa/2.5/hr/
* 38 http://creativecommons.org/licenses/by-sa/2.5/hu/
* 38 http://creativecommons.org/licenses/by-sa/2.5/il/
* 38 http://creativecommons.org/licenses/by-sa/2.5/in/
* 38 http://creativecommons.org/licenses/by-sa/2.5/it/
* 38 http://creativecommons.org/licenses/by-sa/2.5/mk/
* 38 http://creativecommons.org/licenses/by-sa/2.5/mt/
* 38 http://creativecommons.org/licenses/by-sa/2.5/mx/
* 38 http://creativecommons.org/licenses/by-sa/2.5/my/
* 38 http://creativecommons.org/licenses/by-sa/2.5/nl/
* 38 http://creativecommons.org/licenses/by-sa/2.5/pe/
* 38 http://creativecommons.org/licenses/by-sa/2.5/pl/
* 38 http://creativecommons.org/licenses/by-sa/2.5/pt/
* 38 http://creativecommons.org/licenses/by-sa/2.5/se/
* 38 http://creativecommons.org/licenses/by-sa/2.5/si/
* 38 http://creativecommons.org/licenses/by-sa/2.5/tw/
* 38 http://creativecommons.org/licenses/by-sa/2.5/za/ */
{ 0x800000277f9f36f5, 0x01000707, "by-sa/2.5/", 163, -1 },
/* 39 http://creativecommons.org/licenses/by-sa/2.5/scotland/ */
{ 0x0000000080000000, 0x01000703, "by-sa/2.5/", 163, -1 },
/* 40 http://creativecommons.org/licenses/by-sa/3.0/
* 40 http://creativecommons.org/licenses/by-sa/3.0/us/ */
{ 0x8000001000000000, 0x01000707, "by-sa/3.0/", 163, -1 },
/* 41 http://creativecommons.org/licenses/by/1.0/
* 41 http://creativecommons.org/licenses/by/1.0/fi/
* 41 http://creativecommons.org/licenses/by/1.0/il/
* 41 http://creativecommons.org/licenses/by/1.0/nl/ */
{ 0x8000000008044000, 0x01000307, "by/1.0/", 186, 198 },
/* 42 http://creativecommons.org/licenses/by/2.0/
* 42 http://creativecommons.org/licenses/by/2.0/at/
* 42 http://creativecommons.org/licenses/by/2.0/au/
* 42 http://creativecommons.org/licenses/by/2.0/be/
* 42 http://creativecommons.org/licenses/by/2.0/br/
* 42 http://creativecommons.org/licenses/by/2.0/ca/
* 42 http://creativecommons.org/licenses/by/2.0/cl/
* 42 http://creativecommons.org/licenses/by/2.0/de/
* 42 http://creativecommons.org/licenses/by/2.0/es/
* 42 http://creativecommons.org/licenses/by/2.0/fr/
* 42 http://creativecommons.org/licenses/by/2.0/hr/
* 42 http://creativecommons.org/licenses/by/2.0/it/
* 42 http://creativecommons.org/licenses/by/2.0/jp/
* 42 http://creativecommons.org/licenses/by/2.0/kr/
* 42 http://creativecommons.org/licenses/by/2.0/nl/
* 42 http://creativecommons.org/licenses/by/2.0/pl/
* 42 http://creativecommons.org/licenses/by/2.0/tw/
* 42 http://creativecommons.org/licenses/by/2.0/uk/
* 42 http://creativecommons.org/licenses/by/2.0/za/ */
{ 0x8000002c2871a96e, 0x01000307, "by/2.0/", 186, 198 },
/* 43 http://creativecommons.org/licenses/by/2.1/au/ */
{ 0x0000000000000004, 0x01000307, "by/2.1/", 186, 198 },
/* 44 http://creativecommons.org/licenses/by/2.1/es/ */
{ 0x0000000000002000, 0x01000307, "by/2.1/", 186, 198 },
/* 45 http://creativecommons.org/licenses/by/2.1/jp/ */
{ 0x0000000000200000, 0x01000307, "by/2.1/", 186, 198 },
/* 46 http://creativecommons.org/licenses/by/2.5/
* 46 http://creativecommons.org/licenses/by/2.5/ar/
* 46 http://creativecommons.org/licenses/by/2.5/au/
* 46 http://creativecommons.org/licenses/by/2.5/bg/
* 46 http://creativecommons.org/licenses/by/2.5/br/
* 46 http://creativecommons.org/licenses/by/2.5/ca/
* 46 http://creativecommons.org/licenses/by/2.5/ch/
* 46 http://creativecommons.org/licenses/by/2.5/cn/
* 46 http://creativecommons.org/licenses/by/2.5/co/
* 46 http://creativecommons.org/licenses/by/2.5/dk/
* 46 http://creativecommons.org/licenses/by/2.5/es/
* 46 http://creativecommons.org/licenses/by/2.5/hr/
* 46 http://creativecommons.org/licenses/by/2.5/hu/
* 46 http://creativecommons.org/licenses/by/2.5/il/
* 46 http://creativecommons.org/licenses/by/2.5/in/
* 46 http://creativecommons.org/licenses/by/2.5/it/
* 46 http://creativecommons.org/licenses/by/2.5/mk/
* 46 http://creativecommons.org/licenses/by/2.5/mt/
* 46 http://creativecommons.org/licenses/by/2.5/mx/
* 46 http://creativecommons.org/licenses/by/2.5/my/
* 46 http://creativecommons.org/licenses/by/2.5/nl/
* 46 http://creativecommons.org/licenses/by/2.5/pe/
* 46 http://creativecommons.org/licenses/by/2.5/pl/
* 46 http://creativecommons.org/licenses/by/2.5/pt/
* 46 http://creativecommons.org/licenses/by/2.5/se/
* 46 http://creativecommons.org/licenses/by/2.5/si/
* 46 http://creativecommons.org/licenses/by/2.5/tw/
* 46 http://creativecommons.org/licenses/by/2.5/za/ */
{ 0x800000277f9f36f5, 0x01000307, "by/2.5/", 186, 198 },
/* 47 http://creativecommons.org/licenses/by/2.5/scotland/ */
{ 0x0000000080000000, 0x01000303, "by/2.5/", 186, 198 },
/* 48 http://creativecommons.org/licenses/by/3.0/
* 48 http://creativecommons.org/licenses/by/3.0/us/ */
{ 0x8000001000000000, 0x01000307, "by/3.0/", 186, 198 },
/* 49 http://creativecommons.org/licenses/devnations/2.0/ */
{ 0x8000000000000000, 0x01020107, "devnations/2.0/", 277, -1 },
/* 50 http://creativecommons.org/licenses/nc-sa/1.0/
* 50 http://creativecommons.org/licenses/nc-sa/1.0/fi/
* 50 http://creativecommons.org/licenses/nc-sa/1.0/nl/ */
{ 0x8000000008004000, 0x01010507, "nc-sa/1.0/", 296, -1 },
/* 51 http://creativecommons.org/licenses/nc-sa/2.0/jp/ */
{ 0x0000000000200000, 0x01010507, "nc-sa/2.0/", 296, -1 },
/* 52 http://creativecommons.org/licenses/nc-sampling+/1.0/
* 52 http://creativecommons.org/licenses/nc-sampling+/1.0/tw/ */
{ 0x8000000400000000, 0x01010307, "nc-sampling+/1.0/", 321, -1 },
/* 53 http://creativecommons.org/licenses/nc/1.0/
* 53 http://creativecommons.org/licenses/nc/1.0/fi/
* 53 http://creativecommons.org/licenses/nc/1.0/nl/ */
{ 0x8000000008004000, 0x01010107, "nc/1.0/", 349, 363 },
/* 54 http://creativecommons.org/licenses/nc/2.0/jp/ */
{ 0x0000000000200000, 0x01010107, "nc/2.0/", 349, 363 },
/* 55 http://creativecommons.org/licenses/nd-nc/1.0/
* 55 http://creativecommons.org/licenses/nd-nc/1.0/fi/
* 55 http://creativecommons.org/licenses/nd-nc/1.0/nl/ */
{ 0x8000000008004000, 0x01010103, "nd-nc/1.0/", 547, -1 },
/* 56 http://creativecommons.org/licenses/nd-nc/2.0/jp/ */
{ 0x0000000000200000, 0x01010103, "nd-nc/2.0/", 547, -1 },
/* 57 http://creativecommons.org/licenses/nd/1.0/
* 57 http://creativecommons.org/licenses/nd/1.0/fi/
* 57 http://creativecommons.org/licenses/nd/1.0/nl/ */
{ 0x8000000008004000, 0x01000103, "nd/1.0/", 570, 579 },
/* 58 http://creativecommons.org/licenses/nd/2.0/jp/ */
{ 0x0000000000200000, 0x01000103, "nd/2.0/", 570, 579 },
/* 59 http://creativecommons.org/licenses/publicdomain/ */
{ 0x8000000000000000, 0x00000007, "publicdomain/", 712, -1 },
/* 60 http://creativecommons.org/licenses/sa/1.0/
* 60 http://creativecommons.org/licenses/sa/1.0/fi/
* 60 http://creativecommons.org/licenses/sa/1.0/nl/ */
{ 0x8000000008004000, 0x01000507, "sa/1.0/", 726, 737 },
/* 61 http://creativecommons.org/licenses/sa/2.0/jp/ */
{ 0x0000000000200000, 0x01000507, "sa/2.0/", 726, 737 },
/* 62 http://creativecommons.org/licenses/sampling+/1.0/
* 62 http://creativecommons.org/licenses/sampling+/1.0/br/
* 62 http://creativecommons.org/licenses/sampling+/1.0/de/
* 62 http://creativecommons.org/licenses/sampling+/1.0/tw/ */
{ 0x8000000400000820, 0x0100030d, "sampling+/1.0/", 889, -1 },
/* 63 http://creativecommons.org/licenses/sampling/1.0/
* 63 http://creativecommons.org/licenses/sampling/1.0/br/
* 63 http://creativecommons.org/licenses/sampling/1.0/tw/ */
{ 0x8000000400000020, 0x01000305, "sampling/1.0/", 903, -1 },
};
static const gchar license_strings[] =
"GPL\000LGPL\000Attribution-NonCommercial-NoDerivs\000Attribution-NonComme"
"rcial-ShareAlike\000Attribution-NonCommercial\000Attribution-NoDerivs-Non"
"Commercial\000Attribution-NoDerivs\000Attribution-ShareAlike\000Attributi"
"on\000You must attribute the work in the manner specified by the author o"
"r licensor.\000Developing Nations\000NonCommercial-ShareAlike\000NonComme"
"rcial Sampling Plus\000NonCommercial\000The licensor permits others to co"
"py, distribute and transmit the work. In return, licensees may not use th"
"e work for commercial purposes \342\200\224 unless they get the licensor'"
"s permission.\000NoDerivs-NonCommercial\000NoDerivs\000The licensor permi"
"ts others to copy, distribute and transmit only unaltered copies of the w"
"ork \342\200\224 not derivative works based on it.\000Public Domain\000Sh"
"areAlike\000The licensor permits others to distribute derivative works on"
"ly under the same license or one compatible with the one that governs the"
" licensor's work.\000Sampling Plus\000Sampling";

636
gst-libs/gst/tag/licenses.c Normal file
View file

@ -0,0 +1,636 @@
/* GStreamer media licenses utility functions
* Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
*
* 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.
*/
/**
* SECTION:gsttaglicenses
* @short_description: utility functions for Creative Commons licenses
* @see_also: #GstTagList
*
* <refsect2>
* <para>
* Provides information about Creative Commons media licenses, which are
* often expressed in media files as a license URI in tags. Also useful
* for applications creating media files, in case the user wants to license
* the content under a Creative Commons license.
* </para>
* </refsect2>
*/
/* FIXME: add API to check obsolete-ness / replace-by */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <string.h>
#include <stdlib.h>
#include "tag.h"
#include "licenses-tables.dat"
#ifndef GST_DISABLE_GST_DEBUG
#define GST_CAT_DEFAULT ensure_debug_category()
static GstDebugCategory *
ensure_debug_category (void)
{
static gsize cat_gonce = 0;
if (g_once_init_enter (&cat_gonce)) {
gsize cat_done;
cat_done = (gsize) _gst_debug_category_new ("tag-licenses", 0,
"GstTag licenses");
g_once_init_leave (&cat_gonce, cat_done);
}
return (GstDebugCategory *) cat_gonce;
}
#else
#define ensure_debug_category() /* NOOP */
#endif /* GST_DISABLE_GST_DEBUG */
/* -------------------------------------------------------------------------
* Translations
* ------------------------------------------------------------------------- */
#ifdef ENABLE_NLS
static GVariant *
gst_tag_get_license_translations_dictionary (void)
{
static gsize var_gonce = 0;
if (g_once_init_enter (&var_gonce)) {
const gchar *dict_path;
GVariant *var = NULL;
GError *err = NULL;
gchar *data;
gsize len;
/* for gst-uninstalled */
dict_path = g_getenv ("GST_TAG_LICENSE_TRANSLATIONS_DICT");
if (dict_path == NULL)
dict_path = LICENSE_TRANSLATIONS_PATH;
GST_INFO ("Loading license translations from '%s'", dict_path);
if (g_file_get_contents (dict_path, &data, &len, &err)) {
var = g_variant_new_from_data (G_VARIANT_TYPE ("a{sa{ss}}"), data, len,
TRUE, (GDestroyNotify) g_free, data);
} else {
GST_WARNING ("Could not load translation dictionary %s", err->message);
g_error_free (err);
var = g_variant_new_array (G_VARIANT_TYPE ("{sa{ss}}"), NULL, 0);
}
g_once_init_leave (&var_gonce, (gsize) var);
}
return (GVariant *) var_gonce;
}
#endif
#ifdef ENABLE_NLS
#if !GLIB_CHECK_VERSION(2,28,0)
static GVariant *
gst_g_variant_lookup_value (GVariant * dictionary, const gchar * key,
const GVariantType * expected_type)
{
GVariantIter iter;
GVariant *entry;
GVariant *value;
GST_ERROR ("here, using fallback");
g_assert (g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{s*}")));
g_assert (expected_type != NULL);
g_variant_iter_init (&iter, dictionary);
while ((entry = g_variant_iter_next_value (&iter))) {
GVariant *entry_key;
gboolean matches;
entry_key = g_variant_get_child_value (entry, 0);
matches = strcmp (g_variant_get_string (entry_key, NULL), key) == 0;
g_variant_unref (entry_key);
if (matches)
break;
g_variant_unref (entry);
}
if (entry == NULL)
return NULL;
value = g_variant_get_child_value (entry, 1);
g_variant_unref (entry);
if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)) {
GVariant *tmp;
tmp = g_variant_get_variant (value);
g_variant_unref (value);
if (expected_type && !g_variant_is_of_type (tmp, expected_type)) {
g_variant_unref (tmp);
tmp = NULL;
}
value = tmp;
}
g_assert (value == NULL || g_variant_is_of_type (value, expected_type));
return value;
}
#define g_variant_lookup_value gst_g_variant_lookup_value
#endif /* !GLIB_CHECK_VERSION(2,28,0) */
static gboolean
gst_variant_lookup_string_value (GVariant * dict, const gchar * lang,
const gchar ** translation)
{
GVariant *trans;
trans = g_variant_lookup_value (dict, lang, G_VARIANT_TYPE ("s"));
if (trans == NULL)
return FALSE;
*translation = g_variant_get_string (trans, NULL);
/* string will stay valid */
g_variant_unref (trans);
GST_TRACE ("Result: '%s' for language '%s'", *translation, lang);
return TRUE;
}
#endif
static const gchar *
gst_license_str_translate (const gchar * s)
{
#ifdef ENABLE_NLS
GVariant *v, *dict, *trans;
v = gst_tag_get_license_translations_dictionary ();
g_assert (v != NULL);
dict = g_variant_lookup_value (v, s, G_VARIANT_TYPE ("a{ss}"));
if (dict != NULL) {
const gchar *const *lang;
const gchar *env_lang;
/* for unit tests */
if ((env_lang = g_getenv ("GST_TAG_LICENSE_TRANSLATIONS_LANG"))) {
if (gst_variant_lookup_string_value (dict, env_lang, &s))
GST_TRACE ("Result: '%s' for forced language '%s'", s, env_lang);
goto beach;
}
lang = g_get_language_names ();
while (lang != NULL && *lang != NULL) {
GST_TRACE ("Looking up '%s' for language '%s'", s, *lang);
trans = g_variant_lookup_value (dict, *lang, G_VARIANT_TYPE ("s"));
if (trans != NULL) {
s = g_variant_get_string (trans, NULL);
/* s will stay valid */
g_variant_unref (trans);
GST_TRACE ("Result: '%s'", s);
break;
}
GST_TRACE ("No result for '%s' for language '%s'", s, *lang);
++lang;
}
beach:
g_variant_unref (dict);
} else {
GST_WARNING ("No dict for string '%s'", s);
}
#endif
return s;
}
/* -------------------------------------------------------------------------
* License handling
* ------------------------------------------------------------------------- */
#define CC_LICENSE_REF_PREFIX "http://creativecommons.org/licenses/"
/* is this license 'generic' (and a base for any of the supported
* jurisdictions), or jurisdiction-specific only? */
#define JURISDICTION_GENERIC (G_GUINT64_CONSTANT (1) << 63)
static const gchar jurisdictions[] =
"ar\000at\000au\000be\000bg\000br\000ca\000ch\000cl\000cn\000co\000de\000"
"dk\000es\000fi\000fr\000hr\000hu\000il\000in\000it\000jp\000kr\000mk\000"
"mt\000mx\000my\000nl\000pe\000pl\000pt\000scotland\000se\000si\000tw\000"
"uk\000us\000za";
/**
* gst_tag_get_licenses:
*
* Returns a list of known license references (in form of URIs). This is
* useful for UIs to build a list of available licenses for tagging purposes
* (e.g. to tag an audio track appropriately in a video or audio editor, or
* an image in a camera application).
*
* Returns: NULL-terminated array of license strings. Free with g_strfreev()
* when no longer needed.
*
* Since: 0.10.36
*/
gchar **
gst_tag_get_licenses (void)
{
GPtrArray *arr;
int i;
arr = g_ptr_array_new ();
for (i = 0; i < G_N_ELEMENTS (licenses); ++i) {
const gchar *jurs;
gboolean is_generic;
guint64 jbits;
gchar *ref;
jbits = licenses[i].jurisdictions;
is_generic = (jbits & JURISDICTION_GENERIC) != 0;
if (is_generic) {
ref = g_strconcat (CC_LICENSE_REF_PREFIX, licenses[i].ref, NULL);
GST_LOG ("Adding %2d %s (generic)", i, ref);
g_ptr_array_add (arr, ref);
jbits &= ~JURISDICTION_GENERIC;
}
jurs = jurisdictions;
while (jbits != 0) {
if ((jbits & 1)) {
ref = g_strconcat (CC_LICENSE_REF_PREFIX, licenses[i].ref, jurs, "/",
NULL);
GST_LOG ("Adding %2d %s (%s: %s)", i, ref,
(is_generic) ? "derived" : "specific", jurs);
g_ptr_array_add (arr, ref);
}
g_assert (jurs < (jurisdictions + sizeof (jurisdictions)));
jurs += strlen (jurs) + 1;
jbits >>= 1;
}
}
g_ptr_array_add (arr, NULL);
return (gchar **) g_ptr_array_free (arr, FALSE);
}
static gint
gst_tag_get_license_idx (const gchar * license_ref, const gchar ** jurisdiction)
{
const gchar *ref, *jur_suffix;
int i;
GST_TRACE ("Looking up '%s'", license_ref);
if (!g_str_has_prefix (license_ref, CC_LICENSE_REF_PREFIX)) {
GST_WARNING ("unknown license prefix in ref '%s'", license_ref);
return -1;
}
if (jurisdiction != NULL)
*jurisdiction = NULL;
ref = license_ref + sizeof (CC_LICENSE_REF_PREFIX) - 1;
for (i = 0; i < G_N_ELEMENTS (licenses); ++i) {
guint64 jbits = licenses[i].jurisdictions;
const gchar *jurs, *lref = licenses[i].ref;
gsize lref_len = strlen (lref);
/* table should have "foo/bar/" with trailing slash */
g_assert (lref[lref_len - 1] == '/');
if ((jbits & JURISDICTION_GENERIC)) {
GST_TRACE ("[%2d] %s checking generic match", i, licenses[i].ref);
/* exact match? */
if (strcmp (ref, lref) == 0)
return i;
/* exact match but without the trailing slash in ref? */
if (strncmp (ref, lref, lref_len - 1) == 0 && ref[lref_len - 1] == '\0')
return i;
}
if (!g_str_has_prefix (ref, lref))
continue;
GST_TRACE ("[%2d] %s checking jurisdictions", i, licenses[i].ref);
jbits &= ~JURISDICTION_GENERIC;
jur_suffix = ref + lref_len;
if (*jur_suffix == '\0')
continue;
jurs = jurisdictions;
while (jbits != 0) {
guint jur_len = strlen (jurs);
if ((jbits & 1)) {
if (strncmp (jur_suffix, jurs, jur_len) == 0 &&
(jur_suffix[jur_len] == '\0' || jur_suffix[jur_len] == '/')) {
GST_LOG ("matched %s to %s with jurisdiction %s (idx %d)",
license_ref, licenses[i].ref, jurs, i);
if (jurisdiction != NULL)
*jurisdiction = jurs;
return i;
}
}
g_assert (jurs < (jurisdictions + sizeof (jurisdictions)));
jurs += jur_len + 1;
jbits >>= 1;
}
}
GST_WARNING ("unhandled license ref '%s'", license_ref);
return -1;
}
/**
* gst_tag_get_license_flags:
* @license_ref: a license reference string in form of a URI,
* e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
*
* Get the flags of a license, which describe most of the features of
* a license in their most general form.
*
* Returns: the flags of the license, or 0 if the license is unknown
*
* Since: 0.10.36
*/
GstTagLicenseFlags
gst_tag_get_license_flags (const gchar * license_ref)
{
int idx;
g_return_val_if_fail (license_ref != NULL, 0);
idx = gst_tag_get_license_idx (license_ref, NULL);
return (idx < 0) ? 0 : licenses[idx].flags;
}
/**
* gst_tag_get_license_nick:
* @license_ref: a license reference string in form of a URI,
* e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
*
* Get the nick name of a license, which is a short (untranslated) string
* such as e.g. "CC BY-NC-ND 2.0 UK".
*
* Returns: the nick name of the license, or NULL if the license is unknown
*
* Since: 0.10.36
*/
const gchar *
gst_tag_get_license_nick (const gchar * license_ref)
{
GstTagLicenseFlags flags;
const gchar *creator_prefix, *res;
gchar *nick, *c;
g_return_val_if_fail (license_ref != NULL, NULL);
flags = gst_tag_get_license_flags (license_ref);
if ((flags & GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE)) {
creator_prefix = "CC ";
} else if ((flags & GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE)) {
creator_prefix = "FSF ";
} else if (g_str_has_suffix (license_ref, "publicdomain/")) {
creator_prefix = "";
} else {
return NULL;
}
nick = g_strdup_printf ("%s%s", creator_prefix,
license_ref + sizeof (CC_LICENSE_REF_PREFIX) - 1);
g_strdelimit (nick, "/", ' ');
g_strchomp (nick);
for (c = nick; *c != '\0'; ++c)
*c = g_ascii_toupper (*c);
GST_LOG ("%s => nick %s", license_ref, nick);
res = g_intern_string (nick); /* for convenience */
g_free (nick);
return res;
}
/**
* gst_tag_get_license_title:
* @license_ref: a license reference string in form of a URI,
* e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
*
* Get the title of a license, which is a short translated description
* of the license's features (generally not very pretty though).
*
* Returns: the title of the license, or NULL if the license is unknown or
* no title is available.
*
* Since: 0.10.36
*/
const gchar *
gst_tag_get_license_title (const gchar * license_ref)
{
int idx;
g_return_val_if_fail (license_ref != NULL, NULL);
idx = gst_tag_get_license_idx (license_ref, NULL);
if (idx < 0 || licenses[idx].title_idx < 0)
return NULL;
return gst_license_str_translate (&license_strings[licenses[idx].title_idx]);
}
/**
* gst_tag_get_license_description:
* @license_ref: a license reference string in form of a URI,
* e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
*
* Get the description of a license, which is a translated description
* of the license's main features.
*
* Returns: the description of the license, or NULL if the license is unknown
* or a description is not available.
*
* Since: 0.10.36
*/
const gchar *
gst_tag_get_license_description (const gchar * license_ref)
{
int idx;
g_return_val_if_fail (license_ref != NULL, NULL);
idx = gst_tag_get_license_idx (license_ref, NULL);
if (idx < 0 || licenses[idx].desc_idx < 0)
return NULL;
return gst_license_str_translate (&license_strings[licenses[idx].desc_idx]);
}
/**
* gst_tag_get_license_jurisdiction:
* @license_ref: a license reference string in form of a URI,
* e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
*
* Get the jurisdiction code of a license. This is usually a two-letter
* ISO 3166-1 alpha-2 code, but there is also the special case of Scotland,
* for which no code exists and which is thus represented as "scotland".
*
* Known jurisdictions: ar, at, au, be, bg, br, ca, ch, cl, cn, co, de,
* dk, es, fi, fr, hr, hu, il, in, it, jp, kr, mk, mt, mx, my, nl, pe, pl,
* pt, scotland, se, si, tw, uk, us, za.
*
* Returns: the jurisdiction code of the license, or NULL if the license is
* unknown or is not specific to a particular jurisdiction.
*
* Since: 0.10.36
*/
const gchar *
gst_tag_get_license_jurisdiction (const gchar * license_ref)
{
const gchar *jurisdiction;
int idx;
g_return_val_if_fail (license_ref != NULL, NULL);
idx = gst_tag_get_license_idx (license_ref, &jurisdiction);
return (idx < 0) ? NULL : jurisdiction;
}
/**
* gst_tag_get_license_version:
* @license_ref: a license reference string in form of a URI,
* e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
*
* Get the version of a license.
*
* Returns: the version of the license, or NULL if the license is not known or
* has no version
*
* Since: 0.10.36
*/
const gchar *
gst_tag_get_license_version (const gchar * license_ref)
{
int idx;
g_return_val_if_fail (license_ref != NULL, NULL);
idx = gst_tag_get_license_idx (license_ref, NULL);
if (idx < 0)
return NULL;
#define LICENSE_FLAG_CC_OR_FSF \
(GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE|\
GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE)
/* e.g. publicdomain isn't versioned */
if (!(licenses[idx].flags & LICENSE_FLAG_CC_OR_FSF))
return NULL;
/* KISS for now... */
if (strstr (licenses[idx].ref, "/1.0/"))
return "1.0";
else if (strstr (licenses[idx].ref, "/2.0/"))
return "2.0";
else if (strstr (licenses[idx].ref, "/2.1/"))
return "2.1";
else if (strstr (licenses[idx].ref, "/2.5/"))
return "2.5";
else if (strstr (licenses[idx].ref, "/3.0/"))
return "3.0";
GST_ERROR ("Could not determine version for ref '%s'", license_ref);
return NULL;
}
GType
gst_tag_license_flags_get_type (void)
{
/* FIXME: we should really be using glib-mkenums for this.. */
#define C_FLAGS(v) ((guint) v)
static gsize id = 0;
static const GFlagsValue values[] = {
{C_FLAGS (GST_TAG_LICENSE_PERMITS_REPRODUCTION),
"GST_TAG_LICENSE_PERMITS_REPRODUCTION", "permits-reproduction"},
{C_FLAGS (GST_TAG_LICENSE_PERMITS_DISTRIBUTION),
"GST_TAG_LICENSE_PERMITS_DISTRIBUTION", "permits-distribution"},
{C_FLAGS (GST_TAG_LICENSE_PERMITS_DERIVATIVE_WORKS),
"GST_TAG_LICENSE_PERMITS_DERIVATIVE_WORKS",
"permits-derivative-works"},
{C_FLAGS (GST_TAG_LICENSE_PERMITS_SHARING),
"GST_TAG_LICENSE_PERMITS_SHARING", "permits-sharing"},
{C_FLAGS (GST_TAG_LICENSE_REQUIRES_NOTICE),
"GST_TAG_LICENSE_REQUIRES_NOTICE", "requires-notice"},
{C_FLAGS (GST_TAG_LICENSE_REQUIRES_ATTRIBUTION),
"GST_TAG_LICENSE_REQUIRES_ATTRIBUTION", "requires-attributions"},
{C_FLAGS (GST_TAG_LICENSE_REQUIRES_SHARE_ALIKE),
"GST_TAG_LICENSE_REQUIRES_SHARE_ALIKE", "requires-share-alike"},
{C_FLAGS (GST_TAG_LICENSE_REQUIRES_SOURCE_CODE),
"GST_TAG_LICENSE_REQUIRES_SOURCE_CODE", "requires-source-code"},
{C_FLAGS (GST_TAG_LICENSE_REQUIRES_COPYLEFT),
"GST_TAG_LICENSE_REQUIRES_COPYLEFT", "requires-copyleft"},
{C_FLAGS (GST_TAG_LICENSE_REQUIRES_LESSER_COPYLEFT),
"GST_TAG_LICENSE_REQUIRES_LESSER_COPYLEFT",
"requires-lesser-copyleft"},
{C_FLAGS (GST_TAG_LICENSE_PROHIBITS_COMMERCIAL_USE),
"GST_TAG_LICENSE_PROHIBITS_COMMERCIAL_USE",
"prohibits-commercial-use"},
{C_FLAGS (GST_TAG_LICENSE_PROHIBITS_HIGH_INCOME_NATION_USE),
"GST_TAG_LICENSE_PROHIBITS_HIGH_INCOME_NATION_USE",
"prohibits-high-income-nation-use"},
{C_FLAGS (GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE),
"GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE",
"creative-commons-license"},
{C_FLAGS (GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE),
"GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE",
"free-software-foundation-license"},
{0, NULL, NULL}
};
if (g_once_init_enter (&id)) {
GType tmp = g_flags_register_static ("GstTagLicenseFlags", values);
g_once_init_leave (&id, tmp);
}
return (GType) id;
}

View file

@ -0,0 +1,890 @@
/* GStreamer License Utility Functions
* Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
*
* 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.
*/
/* mklicensestables.c:
* little program that reads liblicense's license RDF files and outputs tables
* with the most important information, so we don't have to parse megabytes
* of mostly redundant RDF files to get some basic information (and vendors
* don't have to ship it all).
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tag.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/* TODO: we can merge some of the jurisdiction-only license table entries
* into one entry with multiple jurisdictions and without the 'generic' flag,
* .e.g. by-nc-nd/2.5/es + by-nc-nd/2.5/au => by-nc-nd/2.5/{es,au} */
#define LIBLICENSE_DATA_PREFIX "/usr/share/liblicense/licenses"
static GHashTable *unknown_sources; /* NULL */
static GList *licenses; /* NULL */
/* list of languages used for translations */
static GList *langs; /* NULL */
/* keep in sync with licenses.c */
static const gchar jurisdictions[] =
"ar\000at\000au\000be\000bg\000br\000ca\000ch\000cl\000cn\000co\000de\000"
"dk\000es\000fi\000fr\000hr\000hu\000il\000in\000it\000jp\000kr\000mk\000"
"mt\000mx\000my\000nl\000pe\000pl\000pt\000scotland\000se\000si\000tw\000"
"uk\000us\000za";
/* keep in sync with gst_tag_get_license_version() */
static const gchar known_versions[] = "1.0/2.0/2.1/2.5/3.0/";
/* is this license 'generic' (and a base for any of the supported
* jurisdictions), or jurisdiction-specific only? */
#define JURISDICTION_GENERIC (G_GUINT64_CONSTANT (1) << 63)
typedef struct
{
gchar *ref;
guint64 jurisdiction;
gchar *jurisdiction_suffix; /* if not generic (e.g. "jp/") */
gchar *legalcode;
gchar *version;
gchar *replaced_by;
gchar *source;
GstTagLicenseFlags flags;
gboolean deprecated;
GHashTable *titles;
GHashTable *descriptions;
/* for processing */
const gchar *cur_lang;
gboolean packed_into_source;
/* list of licenses packed into this one (ie. this is the source of those) */
GList *derived;
} License;
static GstTagLicenseFlags
ref_to_flag (const gchar * ref)
{
if (strcmp (ref, "http://creativecommons.org/ns#Reproduction") == 0)
return GST_TAG_LICENSE_PERMITS_REPRODUCTION;
if (strcmp (ref, "http://creativecommons.org/ns#Distribution") == 0)
return GST_TAG_LICENSE_PERMITS_DISTRIBUTION;
if (strcmp (ref, "http://creativecommons.org/ns#DerivativeWorks") == 0)
return GST_TAG_LICENSE_PERMITS_DERIVATIVE_WORKS;
if (strcmp (ref, "http://creativecommons.org/ns#Sharing") == 0)
return GST_TAG_LICENSE_PERMITS_SHARING;
if (strcmp (ref, "http://creativecommons.org/ns#Notice") == 0)
return GST_TAG_LICENSE_REQUIRES_NOTICE;
if (strcmp (ref, "http://creativecommons.org/ns#Attribution") == 0)
return GST_TAG_LICENSE_REQUIRES_ATTRIBUTION;
if (strcmp (ref, "http://creativecommons.org/ns#ShareAlike") == 0)
return GST_TAG_LICENSE_REQUIRES_SHARE_ALIKE;
if (strcmp (ref, "http://creativecommons.org/ns#SourceCode") == 0)
return GST_TAG_LICENSE_REQUIRES_SOURCE_CODE;
if (strcmp (ref, "http://creativecommons.org/ns#Copyleft") == 0)
return GST_TAG_LICENSE_REQUIRES_COPYLEFT;
if (strcmp (ref, "http://creativecommons.org/ns#LesserCopyleft") == 0)
return GST_TAG_LICENSE_REQUIRES_LESSER_COPYLEFT;
if (strcmp (ref, "http://creativecommons.org/ns#CommercialUse") == 0)
return GST_TAG_LICENSE_PROHIBITS_COMMERCIAL_USE;
if (strcmp (ref, "http://creativecommons.org/ns#HighIncomeNationUse") == 0)
return GST_TAG_LICENSE_PROHIBITS_HIGH_INCOME_NATION_USE;
g_error ("Unknown permits/requires/prohibits: %s\n", ref);
return 0;
};
static guint64
ref_to_jurisdiction (const gchar * ref)
{
const gchar *j = jurisdictions;
gchar *jur;
guint64 bit = 1;
jur = g_strdup (ref + strlen ("http://creativecommons.org/international/"));
g_strdelimit (jur, "/", '\0');
while (j < jurisdictions + sizeof (jurisdictions)) {
if (strcmp (j, jur) == 0) {
g_free (jur);
g_assert (bit != 0 && bit != JURISDICTION_GENERIC);
return bit;
}
j += strlen (j) + 1;
bit <<= 1;
}
g_error ("Unknown jurisdiction '%s'\n", ref);
}
typedef enum
{
TAG_CC_LICENSE,
TAG_CC_JURISDICTION,
TAG_CC_LEGALCODE,
TAG_CC_PROHIBITS,
TAG_CC_REQUIRES,
TAG_CC_PERMITS,
TAG_CC_DEPRECATED_ON,
TAG_DC_CREATOR,
TAG_DC_SOURCE,
TAG_DC_TITLE,
TAG_DC_DESCRIPTION,
TAG_DCQ_HAS_VERSION,
TAG_DCQ_IS_REPLACED_BY,
TAG_RDF_RDF,
TAG_RDF_DESCRIPTION,
} Tag;
static const struct
{
const gchar *element_name;
const gchar *attribute;
const Tag element_tag;
} tag_map[] = {
{
"cc:License", "rdf:about", TAG_CC_LICENSE}, {
"cc:deprecatedOn", "rdf:datatype", TAG_CC_DEPRECATED_ON}, {
"cc:jurisdiction", "rdf:resource", TAG_CC_JURISDICTION}, {
"cc:legalcode", "rdf:resource", TAG_CC_LEGALCODE}, {
"cc:prohibits", "rdf:resource", TAG_CC_PROHIBITS}, {
"cc:requires", "rdf:resource", TAG_CC_REQUIRES}, {
"cc:permits", "rdf:resource", TAG_CC_PERMITS}, {
"dc:creator", "rdf:resource", TAG_DC_CREATOR}, {
"dc:source", "rdf:resource", TAG_DC_SOURCE}, {
"dc:title", "xml:lang", TAG_DC_TITLE}, {
"dc:description", "xml:lang", TAG_DC_DESCRIPTION}, {
"dcq:hasVersion", NULL, TAG_DCQ_HAS_VERSION}, {
"dcq:isReplacedBy", "rdf:resource", TAG_DCQ_IS_REPLACED_BY}, {
"rdf:RDF", NULL, TAG_RDF_RDF}, {
"rdf:Description", "rdf:about", TAG_RDF_DESCRIPTION},
/* these three are just for by-nc-nd_2.0_jp_.rdf */
{
"dc:isBasedOn", "rdf:resource", TAG_DC_SOURCE}, {
"dc:hasVersion", NULL, TAG_DCQ_HAS_VERSION}, {
"dc:isReplacedBy", "rdf:resource", TAG_DCQ_IS_REPLACED_BY}
};
static void
parse_start (GMarkupParseContext * ctx, const gchar * element_name,
const gchar ** attr_names, const gchar ** attr_vals,
gpointer user_data, GError ** err)
{
License *license = user_data;
const gchar *ref = NULL;
int i;
for (i = 0; i < G_N_ELEMENTS (tag_map); ++i) {
if (strcmp (element_name, tag_map[i].element_name) == 0)
break;
}
if (i == G_N_ELEMENTS (tag_map))
g_error ("Unexpected tag '%s'\n", element_name);
if (tag_map[i].attribute == NULL)
return;
if (!g_markup_collect_attributes (element_name, attr_names, attr_vals,
err, G_MARKUP_COLLECT_STRING, tag_map[i].attribute, &ref,
G_MARKUP_COLLECT_INVALID)) {
return;
}
switch (tag_map[i].element_tag) {
case TAG_CC_LICENSE:
if (!g_str_has_prefix (ref, "http://creativecommons.org/licenses/"))
g_error ("Unexpected license reference: %s\n", ref);
/* we assume one license per file, and CC license ref */
g_assert (license->ref == NULL);
license->ref = g_strdup (ref);
break;
case TAG_CC_JURISDICTION:
if (!g_str_has_prefix (ref, "http://creativecommons.org/international/"))
g_error ("Unknown license jurisdiction: %s\n", ref);
/* we assume one jurisdiction per license */
g_assert (license->jurisdiction == JURISDICTION_GENERIC);
license->jurisdiction = ref_to_jurisdiction (ref);
license->jurisdiction_suffix =
g_strdup (ref + strlen ("http://creativecommons.org/international/"));
break;
case TAG_CC_LEGALCODE:
if (!g_str_has_prefix (ref, "http://creativecommons.org/licenses/"))
g_error ("Unexpected legalcode reference: %s\n", ref);
/* we assume one legalcode per license */
g_assert (license->legalcode == NULL);
license->legalcode = g_strdup (ref);
break;
case TAG_DC_CREATOR:
if (strcmp (ref, "http://creativecommons.org") == 0) {
license->flags |= GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE;
} else if (strcmp (ref, "http://fsf.org") == 0) {
license->flags |= GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE;
} else {
g_error ("Unknown license creator: %s\n", ref);
}
break;
case TAG_CC_DEPRECATED_ON:
break;
case TAG_CC_PROHIBITS:
case TAG_CC_REQUIRES:
case TAG_CC_PERMITS:
license->flags |= ref_to_flag (ref);
break;
case TAG_DC_TITLE:{
gchar *cur_lang;
cur_lang = g_strdelimit (g_strdup (ref), "-", '_');
license->cur_lang = g_intern_string (cur_lang);
if (!g_list_find_custom (langs, cur_lang, (GCompareFunc) strcmp))
langs = g_list_prepend (langs, (gpointer) license->cur_lang);
g_free (cur_lang);
break;
}
case TAG_DC_DESCRIPTION:{
gchar *cur_lang;
cur_lang = g_strdelimit (g_strdup (ref), "-", '_');
license->cur_lang = g_intern_string (cur_lang);
if (!g_list_find_custom (langs, cur_lang, (GCompareFunc) strcmp))
langs = g_list_prepend (langs, (gpointer) license->cur_lang);
g_free (cur_lang);
break;
}
case TAG_DCQ_IS_REPLACED_BY:
/* we assume one replacer per license for now */
g_assert (license->replaced_by == NULL);
license->replaced_by = g_strdup (ref);
break;
case TAG_RDF_DESCRIPTION:
if (!g_str_has_prefix (ref, "http://creativecommons.org/licenses/"))
g_error ("Unexpected license reference: %s\n", ref);
if (license->ref != NULL && strcmp (license->ref, ref) != 0) {
gchar *f, *r = g_strdup (ref);
/* work around bug in some of the RDFs ... */
if ((f = strstr (r, "by-nc-nd"))) {
memcpy (f, "by-nd-nc", 8);
}
if (strcmp (license->ref, r) != 0) {
g_error ("rdf:Description chunk for other than current license");
}
g_free (r);
}
break;
case TAG_DC_SOURCE:
if (!g_str_has_prefix (ref, "http://creativecommons.org/licenses/"))
g_error ("Unexpected source reference: %s\n", ref);
/* we assume one source (for jurisdiction-specific versions) */
g_assert (license->source == NULL);
license->source = g_strdup (ref);
break;
default:
g_printerr ("unhandled start tag: %s\n", element_name);
break;
}
}
static void
parse_text (GMarkupParseContext * ctx, const gchar * text, gsize text_len,
gpointer user_data, GError ** err)
{
License *license = user_data;
const gchar *element_name, *found;
int i;
element_name = g_markup_parse_context_get_element (ctx);
for (i = 0; i < G_N_ELEMENTS (tag_map); ++i) {
if (strcmp (element_name, tag_map[i].element_name) == 0)
break;
}
if (i == G_N_ELEMENTS (tag_map))
g_error ("Unexpected tag '%s'\n", element_name);
switch (tag_map[i].element_tag) {
case TAG_CC_LICENSE:
case TAG_CC_JURISDICTION:
case TAG_CC_LEGALCODE:
case TAG_DC_CREATOR:
case TAG_CC_PROHIBITS:
case TAG_CC_REQUIRES:
case TAG_CC_PERMITS:
case TAG_RDF_RDF:
case TAG_RDF_DESCRIPTION:
break;
case TAG_DC_TITLE:
if (license->titles == NULL) {
license->titles = g_hash_table_new (g_str_hash, g_str_equal);
}
g_hash_table_insert (license->titles, (gpointer) license->cur_lang,
(gpointer) g_intern_string (text));
break;
case TAG_DC_DESCRIPTION:{
gchar *txt = g_strdup (text);
if (license->descriptions == NULL) {
license->descriptions = g_hash_table_new (g_str_hash, g_str_equal);
}
g_strdelimit (txt, "\n", ' ');
g_hash_table_insert (license->descriptions, (gpointer) license->cur_lang,
(gpointer) g_intern_string (txt));
g_free (txt);
break;
}
case TAG_DCQ_HAS_VERSION:
/* we assume one version per license */
g_assert (license->version == NULL);
license->version = g_strdup (text);
found = strstr (known_versions, license->version);
if (found == NULL || found[strlen (license->version)] != '/')
g_error ("Unexpected version '%s', please add to table.", text);
break;
case TAG_CC_DEPRECATED_ON:
license->deprecated = TRUE;
break;
case TAG_DC_SOURCE: // FIXME
default:
g_print ("text (%s) (%s): '%s'\n", element_name, license->cur_lang, text);
}
}
static void
parse_passthrough (GMarkupParseContext * ctx, const gchar * text, gsize len,
gpointer user_data, GError ** err)
{
if (!g_str_has_prefix (text, "<?xml ")) {
g_error ("Unexpected passthrough text: %s\n", text);
}
}
static void
parse_error (GMarkupParseContext * ctx, GError * err, gpointer data)
{
g_error ("parse error: %s\n", err->message);
}
static const GMarkupParser license_rdf_parser = {
parse_start, NULL, parse_text, parse_passthrough, parse_error
};
static void
parse_license_rdf (const gchar * fn, const gchar * rdf)
{
GMarkupParseContext *ctx;
License *license;
GError *err = NULL;
if (!g_utf8_validate (rdf, -1, NULL)) {
g_error ("%s is not valid UTF-8\n", fn);
}
license = g_new0 (License, 1);
/* mark as generic until proven otherwise */
license->jurisdiction = JURISDICTION_GENERIC;
ctx = g_markup_parse_context_new (&license_rdf_parser,
G_MARKUP_TREAT_CDATA_AS_TEXT, license, NULL);
/* g_print ("Parsing %s\n", fn); */
if (!g_markup_parse_context_parse (ctx, rdf, -1, &err)) {
g_error ("Error parsing file %s: %s\n", fn, err->message);
}
licenses = g_list_append (licenses, license);
g_markup_parse_context_free (ctx);
}
static void
read_licenses (const gchar * licenses_dir)
{
const gchar *name;
GError *err = NULL;
GDir *dir;
dir = g_dir_open (licenses_dir, 0, &err);
if (dir == NULL)
g_error ("Failed to g_dir_open('%s'): %s", licenses_dir, err->message);
while ((name = g_dir_read_name (dir))) {
gchar *fn, *rdf;
fn = g_build_filename (licenses_dir, name, NULL);
if (g_file_get_contents (fn, &rdf, NULL, &err)) {
parse_license_rdf (fn, rdf);
g_free (rdf);
} else {
g_printerr ("Could not read file '%s': %s\n", fn, err->message);
g_error_free (err);
err = NULL;
}
g_free (fn);
}
g_dir_close (dir);
}
static License *
find_license (const gchar * ref)
{
GList *l;
if (!g_str_has_prefix (ref, "http://creativecommons.org/"))
return NULL;
for (l = licenses; l != NULL; l = l->next) {
License *license = l->data;
if (strcmp (license->ref, ref) == 0)
return license;
}
return NULL;
}
static int
license_ref_cmp (License * a, License * b)
{
return strcmp (a->ref, b->ref);
}
#define STRING_TABLE_MAX_STRINGS 100
typedef struct
{
GString *s;
guint num_escaped;
guint num_strings;
guint indices[STRING_TABLE_MAX_STRINGS];
gchar *strings[STRING_TABLE_MAX_STRINGS]; /* unescaped strings */
} StringTable;
static StringTable *
string_table_new (void)
{
StringTable *t = g_new0 (StringTable, 1);
t->s = g_string_new (NULL);
return t;
}
static void
string_table_free (StringTable * t)
{
int i;
for (i = 0; i < t->num_strings; ++i)
g_free (t->strings[i]);
g_string_free (t->s, TRUE);
g_free (t);
}
static guint
string_table_add_string (StringTable * t, const gchar * str)
{
const gchar *s;
guint idx, i;
/* check if we already have this string */
for (i = 0; i < t->num_strings; ++i) {
if (strcmp (t->strings[i], str) == 0)
return t->indices[i];
}
/* save current offset */
idx = t->s->len;
/* adjust for fact that \000 is 4 chars now but will take up only 1 later */
idx -= t->num_escaped * 3;
/* append one char at a time, making sure to escape UTF-8 characters */
for (s = str; s != NULL && *s != '\0'; ++s) {
if (g_ascii_isprint (*s) && *s != '"' && *s != '\\') {
g_string_append_c (t->s, *s);
} else {
g_string_append_printf (t->s, "\\%03o", (unsigned char) *s);
t->num_escaped++;
}
}
g_string_append (t->s, "\\000");
t->num_escaped++;
t->indices[t->num_strings] = idx;
t->strings[t->num_strings] = g_strdup (str);
++t->num_strings;
return idx;
}
static void
string_table_print (StringTable * t)
{
const gchar *s;
s = t->s->str;
while (s != NULL && *s != '\0') {
gchar line[74], *lastesc;
guint left;
left = strlen (s);
g_strlcpy (line, s, MIN (left, sizeof (line)));
s += sizeof (line) - 1;
/* avoid partial escaped codes at the end of a line */
if ((lastesc = strrchr (line, '\\')) && strlen (lastesc) < 4) {
s -= strlen (lastesc);
*lastesc = '\0';
}
g_print (" \"%s\"", line);
if (left < 74)
break;
g_print ("\n");
}
g_print (";\n");
}
/* skip translation if translated string for e.g. "fr_ca" is same as for "fr" */
static gboolean
skip_translation (GHashTable * ht_strings, const gchar * lang,
const gchar * trans)
{
const gchar *simple_trans;
gchar *simple_lang;
if (strchr (lang, '_') == NULL)
return FALSE;
simple_lang = g_strdup (lang);
g_strdelimit (simple_lang, "_", '\0');
simple_trans = g_hash_table_lookup (ht_strings, (gpointer) simple_lang);
g_free (simple_lang);
return (simple_trans != NULL && strcmp (trans, simple_trans) == 0);
}
static GVariant *
create_translation_dict (GHashTable * ht_strings, const gchar * en)
{
GVariantBuilder array;
guint count = 0;
GList *l;
g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
for (l = langs; l != NULL; l = l->next) {
const gchar *trans, *lang;
lang = (const gchar *) l->data;
trans = g_hash_table_lookup (ht_strings, (gpointer) lang);
if (trans != NULL && *trans != '\0' && strcmp (en, trans) != 0 &&
!skip_translation (ht_strings, lang, trans)) {
/* g_print ("%s (%s) => %s\n", en, lang, trans); */
g_variant_builder_add_value (&array,
g_variant_new_dict_entry (g_variant_new_string (lang),
g_variant_new_string (trans)));
++count;
}
}
if (count == 0) {
g_variant_builder_clear (&array);
return NULL;
}
return g_variant_builder_end (&array);
}
static void
write_translations_dictionary (GList * licenses, const gchar * dict_filename)
{
/* maps C string => (dictionary of: locale => translation) */
GVariantBuilder array;
/* maps C string => boolean (if it's in the dictionary already */
GHashTable *translations;
GVariant *var;
GList *l;
FILE *f;
/* sort langs for prettiness / to make variant dumps easier to read */
langs = g_list_sort (langs, (GCompareFunc) strcmp);
g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
translations = g_hash_table_new (g_str_hash, g_str_equal);
for (l = licenses; l != NULL; l = l->next) {
const gchar *en;
License *license;
license = l->data;
if (license->packed_into_source)
continue;
/* add title + translations */
en = g_hash_table_lookup (license->titles, "en");
g_assert (en != NULL);
/* check if we already have added translations for this string */
if (!g_hash_table_lookup (translations, (gpointer) en)) {
GVariant *trans;
trans = create_translation_dict (license->titles, en);
if (trans != NULL) {
g_variant_builder_add_value (&array,
g_variant_new_dict_entry (g_variant_new_string (en), trans));
g_hash_table_insert (translations, (gpointer) en,
GINT_TO_POINTER (TRUE));
}
}
/* add description + translations */
if (license->descriptions == NULL)
continue;
en = g_hash_table_lookup (license->descriptions, "en");
g_assert (en != NULL);
/* check if we already have added translations for this string */
if (!g_hash_table_lookup (translations, (gpointer) en)) {
GVariant *trans;
trans = create_translation_dict (license->descriptions, en);
if (trans != NULL) {
g_variant_builder_add_value (&array,
g_variant_new_dict_entry (g_variant_new_string (en), trans));
g_hash_table_insert (translations, (gpointer) en,
GINT_TO_POINTER (TRUE));
}
}
}
var = g_variant_builder_end (&array);
f = fopen (dict_filename, "wb");
if (fwrite (g_variant_get_data (var), g_variant_get_size (var), 1, f) != 1) {
g_error ("failed to write dict to file: %s", g_strerror (errno));
}
fclose (f);
g_printerr ("Wrote dictionary to %s, size: %u, type: %s\n", dict_filename,
(guint) g_variant_get_size (var), (gchar *) g_variant_get_type (var));
g_variant_unref (var);
g_hash_table_destroy (translations);
}
int
main (int argc, char **argv)
{
gchar *translation_dict_fn = NULL;
GOptionContext *ctx;
GOptionEntry options[] = {
{"translation-dictionary", 0, 0, G_OPTION_ARG_FILENAME,
&translation_dict_fn, "Filename of translations dictionary to write",
NULL},
{NULL}
};
StringTable *string_table;
GError *err = NULL;
GList *l;
int idx = 0;
g_type_init ();
ctx = g_option_context_new ("");
g_option_context_add_main_entries (ctx, options, NULL);
if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
g_printerr ("Error initializing: %s\n", err->message);
exit (1);
}
g_option_context_free (ctx);
read_licenses (LIBLICENSE_DATA_PREFIX);
g_printerr ("%d licenses\n", g_list_length (licenses));
unknown_sources = g_hash_table_new (g_str_hash, g_str_equal);
for (l = licenses; l != NULL; l = l->next) {
License *license = l->data;
/* if the license has as source, check if we can 'pack' it into the
* original license as a jurisdiction-specific variant */
if (license->source != NULL) {
License *source = find_license (license->source);
if (source != NULL) {
if (source->flags != license->flags) {
g_printerr ("Source and derived license have different flags:\n"
"\t0x%08x : %s\n\t0x%08x : %s\n", source->flags, source->ref,
license->flags, license->ref);
source = NULL;
} else {
if (source->descriptions == NULL) {
/* neither should the derived one then */
g_assert (license->descriptions == NULL);
} else {
/* make sure we're not settling for fewer descriptions than
* there are */
g_assert (g_hash_table_size (license->titles) <=
g_hash_table_size (source->titles));
g_assert (g_hash_table_size (license->descriptions) <=
g_hash_table_size (source->descriptions));
}
}
} else {
/* a source is referenced that we haven't encountered
* (possibly a referencing bug? seems to happen e.g. when there's a
* 2.1 version of a jurisdiction license and it refers to a 2.1
* source version, but there's only a 2.0 or 2.5 source version. So
* maybe it's supposed to refer to the 2.0 source then, who knows) */
if (!g_hash_table_lookup (unknown_sources, license->source)) {
g_printerr ("Unknown source license %s\n", license->source);
g_hash_table_insert (unknown_sources, g_strdup (license->source),
GUINT_TO_POINTER (TRUE));
}
/* g_print ("Unknown source license %s referenced from %s\n",
* license->source, license->ref); */
}
/* should we pack this into the source or not */
if (source != NULL) {
source->jurisdiction |= license->jurisdiction;
source->derived = g_list_insert_sorted (source->derived, license,
(GCompareFunc) license_ref_cmp);
license->packed_into_source = TRUE;
}
} else {
/* no source license */
if (license->titles == NULL)
g_error ("License has no titles: %s\n", license->ref);
if (license->descriptions == NULL);
g_printerr ("License %s has no descriptions!\n", license->ref);
}
}
licenses = g_list_sort (licenses, (GCompareFunc) license_ref_cmp);
string_table = string_table_new ();
g_print ("/* created by mklicensestables.c */\n");
g_print ("static const struct {\n"
" /* jurisdictions in addition to the generic version, bitfield */\n"
" const guint64 jurisdictions;\n"
" const GstTagLicenseFlags flags;\n"
" /* the bit after http://creativecommons.org/licenses/ */\n"
" const gchar ref[18];\n"
" gint16 title_idx; /* index in string table */\n"
" gint16 desc_idx; /* index in string table */\n"
"} licenses[] = {\n");
for (l = licenses; l != NULL; l = l->next) {
const gchar *title_en, *desc_en;
int idx_title, idx_desc;
License *license;
license = l->data;
if (license->packed_into_source)
continue;
title_en = g_hash_table_lookup (license->titles, "en");
g_assert (title_en != NULL);
idx_title = string_table_add_string (string_table, title_en);
g_assert (idx_title <= G_MAXINT16);
if (license->descriptions != NULL) {
desc_en = g_hash_table_lookup (license->descriptions, "en");
g_assert (desc_en != NULL);
idx_desc = string_table_add_string (string_table, desc_en);
g_assert (idx_desc <= G_MAXINT16);
} else {
idx_desc = -1;
}
/* output comments with license refs covered by the next stanza */
if (license->derived != NULL) {
GList *d;
g_print (" /* %2d %s\n", idx, license->ref);
for (d = license->derived; d != NULL; d = d->next) {
License *derived_license = d->data;
g_print (" * %2d %s%s\n", idx, derived_license->ref,
(d->next == NULL) ? " */" : "");
}
} else {
g_print (" /* %2d %s */\n", idx, license->ref);
}
/* output essential data */
{
gchar *ref;
ref =
g_strdup (license->ref +
strlen ("http://creativecommons.org/licenses/"));
/* remove jurisdiction suffix from ref if this is non-generic, since
* the suffix is already contained in the jurisdiction flags */
if (license->jurisdiction_suffix != NULL) {
gsize suffix_len = strlen (license->jurisdiction_suffix);
gchar *cutoff;
cutoff = ref + strlen (ref) - suffix_len;
g_assert (!strncmp (cutoff, license->jurisdiction_suffix, suffix_len));
g_assert (cutoff[suffix_len - 1] == '/');
g_assert (cutoff[suffix_len] == '\0');
*cutoff = '\0';
}
g_print (" { 0x%016" G_GINT64_MODIFIER "x, 0x%08x, \"%s\", %d, %d }%s\n",
license->jurisdiction, license->flags, ref, idx_title, idx_desc,
(l->next != NULL) ? "," : "");
g_free (ref);
}
++idx;
}
g_print ("};\n");
g_print ("\nstatic const gchar license_strings[] =\n");
string_table_print (string_table);
string_table_free (string_table);
string_table = NULL;
if (translation_dict_fn != NULL) {
write_translations_dictionary (licenses, translation_dict_fn);
}
return 0;
}

View file

@ -1,5 +1,6 @@
/* GStreamer
* Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
* Copyright (C) 2006-2011 Tim-Philipp Müller <tim centricular net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -441,6 +442,14 @@ typedef enum {
#define GST_TYPE_TAG_IMAGE_TYPE (gst_tag_image_type_get_type ())
GType gst_tag_image_type_get_type (void);
/**
* GST_TAG_ID3V2_HEADER_SIZE:
*
* ID3V2 header size considered minimum input for some functions.
*
* Since: 0.10.36
*/
#define GST_TAG_ID3V2_HEADER_SIZE 10
/* functions for vorbis comment manipulation */
@ -470,6 +479,10 @@ GstBuffer * gst_tag_list_to_vorbiscomment_buffer (const GstTagLis
/* functions for ID3 tag manipulation */
/* FIXME 0.11: inconsistent API naming: gst_tag_list_new_from_id3v1(), gst_tag_list_from_*_buffer(),
* gst_tag_list_from_id3v2_tag(). Also, note gst.tag.list_xyz() namespace vs. gst.tag_list_xyz(),
* which is a bit confusing and possibly doesn't map too well */
guint gst_tag_id3_genre_count (void);
const gchar * gst_tag_id3_genre_get (const guint id);
GstTagList * gst_tag_list_new_from_id3v1 (const guint8 * data);
@ -484,6 +497,10 @@ gboolean gst_tag_list_add_id3_image (GstTagList * tag_list,
guint image_data_len,
guint id3_picture_type);
GstTagList * gst_tag_list_from_id3v2_tag (GstBuffer * buffer);
guint gst_tag_get_id3v2_tag_size (GstBuffer * buffer);
/* functions to convert GstBuffers with xmp packets contents to GstTagLists and back */
GstTagList * gst_tag_list_from_xmp_buffer (GstBuffer * buffer);
GstBuffer * gst_tag_list_to_xmp_buffer (const GstTagList * list,
@ -549,6 +566,84 @@ const gchar * gst_tag_get_language_code_iso_639_2T (const gchar * lang_code);
#define gst_tag_get_language_code(lang_code) \
gst_tag_get_language_code_iso_639_1(lang_code)
/* functions to deal with (mostly) Creative Commons licenses */
/**
* GstTagLicenseFlags:
* @GST_TAG_LICENSE_PERMITS_REPRODUCTION: making multiple copies
* is allowed
* @GST_TAG_LICENSE_PERMITS_DISTRIBUTION: distribution, public display
* and public performance are allowed
* @GST_TAG_LICENSE_PERMITS_DERIVATIVE_WORKS: distribution of derivative
* works is allowed
* @GST_TAG_LICENSE_PERMITS_SHARING: commercial derivatives are allowed,
* but only non-commercial distribution is allowed
* @GST_TAG_LICENSE_REQUIRES_NOTICE: copyright and license notices
* must be kept intact
* @GST_TAG_LICENSE_REQUIRES_ATTRIBUTION: credit must be given to
* copyright holder and/or author
* @GST_TAG_LICENSE_REQUIRES_SHARE_ALIKE: derivative works must be
* licensed under the same terms or compatible terms as the original work
* @GST_TAG_LICENSE_REQUIRES_SOURCE_CODE: source code (the preferred
* form for making modifications) must be provided when exercising some
* rights granted by the license
* @GST_TAG_LICENSE_REQUIRES_COPYLEFT: derivative and combined works
* must be licensed under specified terms, similar to those of the original
* work
* @GST_TAG_LICENSE_REQUIRES_LESSER_COPYLEFT: derivative works must be
* licensed under specified terms, with at least the same conditions as
* the original work; combinations with the work may be licensed under
* different terms
* @GST_TAG_LICENSE_PROHIBITS_COMMERCIAL_USE: exercising rights for
* commercial purposes is prohibited
* @GST_TAG_LICENSE_PROHIBITS_HIGH_INCOME_NATION_USE: use in a
* non-developing country is prohibited
* @GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE: this license was created
* by the Creative Commons project
* @GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE: this license was
* created by the Free Software Foundation (FSF)
*
* See http://creativecommons.org/ns for more information.
*
* Since: 0.10.36
*/
typedef enum {
GST_TAG_LICENSE_PERMITS_REPRODUCTION = (1 << 0),
GST_TAG_LICENSE_PERMITS_DISTRIBUTION = (1 << 1),
GST_TAG_LICENSE_PERMITS_DERIVATIVE_WORKS = (1 << 2),
GST_TAG_LICENSE_PERMITS_SHARING = (1 << 3),
GST_TAG_LICENSE_REQUIRES_NOTICE = (1 << 8),
GST_TAG_LICENSE_REQUIRES_ATTRIBUTION = (1 << 9),
GST_TAG_LICENSE_REQUIRES_SHARE_ALIKE = (1 << 10),
GST_TAG_LICENSE_REQUIRES_SOURCE_CODE = (1 << 11),
GST_TAG_LICENSE_REQUIRES_COPYLEFT = (1 << 12),
GST_TAG_LICENSE_REQUIRES_LESSER_COPYLEFT = (1 << 13),
GST_TAG_LICENSE_PROHIBITS_COMMERCIAL_USE = (1 << 16),
GST_TAG_LICENSE_PROHIBITS_HIGH_INCOME_NATION_USE = (1 << 17),
GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE = (1 << 24),
GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE = (1 << 25)
} GstTagLicenseFlags;
gchar ** gst_tag_get_licenses (void);
GstTagLicenseFlags gst_tag_get_license_flags (const gchar * license_ref);
const gchar * gst_tag_get_license_nick (const gchar * license_ref);
const gchar * gst_tag_get_license_title (const gchar * license_ref);
const gchar * gst_tag_get_license_version (const gchar * license_ref);
const gchar * gst_tag_get_license_description (const gchar * license_ref);
const gchar * gst_tag_get_license_jurisdiction (const gchar * license_ref);
GType gst_tag_license_flags_get_type (void);
G_END_DECLS
#endif /* __GST_TAG_TAG_H__ */

View file

@ -26,6 +26,7 @@
#include <gst/base/gsttypefindhelper.h>
#include <gst/gst.h>
#include "tag.h"
#include "id3v2.h"
#include <string.h>
@ -189,6 +190,10 @@ gst_tag_register_tags_internal (gpointer unused)
G_TYPE_DOUBLE, _("image vertical ppi"),
_("Media (image/video) intended vertical pixel density in ppi"), NULL);
gst_tag_register (GST_TAG_ID3V2_FRAME, GST_TAG_FLAG_META,
GST_TYPE_BUFFER, _("ID3v2 frame"), _("unparsed id3v2 tag frame"),
gst_tag_merge_use_first);
return NULL;
}

View file

@ -666,6 +666,9 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
adder = GST_ADDER (gst_pad_get_parent (pad));
GST_DEBUG_OBJECT (pad, "Got %s event on src pad",
GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
{
@ -709,7 +712,7 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
* forwarding the seek upstream or from gst_adder_collected,
* whichever happens first.
*/
adder->flush_stop_pending = TRUE;
g_atomic_int_set (&adder->flush_stop_pending, TRUE);
}
GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event);
@ -738,6 +741,9 @@ gst_adder_src_event (GstPad * pad, GstEvent * event)
GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
event);
/* we're forwarding seek to all upstream peers and wait for one to reply
* with a newsegment-event before we send a newsegment-event downstream */
g_atomic_int_set (&adder->wait_for_new_segment, TRUE);
result = forward_event (adder, event, flush);
if (!result) {
/* seek failed. maybe source is a live source. */
@ -780,8 +786,8 @@ gst_adder_sink_event (GstPad * pad, GstEvent * event)
adder = GST_ADDER (gst_pad_get_parent (pad));
GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event),
GST_DEBUG_PAD_NAME (pad));
GST_DEBUG_OBJECT (pad, "Got %s event on sink pad",
GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
@ -803,8 +809,8 @@ gst_adder_sink_event (GstPad * pad, GstEvent * event)
* flush-stop from the collect function later.
*/
GST_OBJECT_LOCK (adder->collect);
adder->segment_pending = TRUE;
adder->flush_stop_pending = FALSE;
g_atomic_int_set (&adder->new_segment_pending, TRUE);
g_atomic_int_set (&adder->flush_stop_pending, FALSE);
/* Clear pending tags */
if (adder->pending_events) {
g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
@ -819,6 +825,14 @@ gst_adder_sink_event (GstPad * pad, GstEvent * event)
adder->pending_events = g_list_append (adder->pending_events, event);
GST_OBJECT_UNLOCK (adder->collect);
goto beach;
case GST_EVENT_NEWSEGMENT:
if (g_atomic_int_compare_and_exchange (&adder->wait_for_new_segment,
TRUE, FALSE)) {
/* make sure we push a new segment, to inform about new basetime
* see FIXME in gst_adder_collected() */
g_atomic_int_set (&adder->new_segment_pending, TRUE);
}
break;
default:
break;
}
@ -1187,7 +1201,8 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
/* we had an output buffer, unref the gapbuffer we kept */
gst_buffer_unref (gapbuf);
if (adder->segment_pending) {
if (g_atomic_int_compare_and_exchange (&adder->new_segment_pending, TRUE,
FALSE)) {
GstEvent *event;
/* FIXME, use rate/applied_rate as set on all sinkpads.
@ -1216,7 +1231,6 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
GST_WARNING_OBJECT (adder->srcpad, "Sending event %p (%s) failed.",
event, GST_EVENT_TYPE_NAME (event));
}
adder->segment_pending = FALSE;
} else {
GST_WARNING_OBJECT (adder->srcpad, "Creating new segment event for "
"start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT " failed",
@ -1304,7 +1318,8 @@ gst_adder_change_state (GstElement * element, GstStateChange transition)
adder->segment.position = 0;
adder->offset = 0;
adder->flush_stop_pending = FALSE;
adder->segment_pending = TRUE;
adder->new_segment_pending = TRUE;
adder->wait_for_new_segment = FALSE;
gst_segment_init (&adder->segment, GST_FORMAT_TIME);
gst_collect_pads_start (adder->collect);
break;

View file

@ -84,10 +84,11 @@ struct _GstAdder {
/* sink event handling */
GstPadEventFunction collect_event;
GstSegment segment;
gboolean segment_pending;
volatile gboolean new_segment_pending;
volatile gboolean wait_for_new_segment;
/* src event handling */
gboolean flush_stop_pending;
volatile gboolean flush_stop_pending;
/* target caps */
GstCaps *filter_caps;

View file

@ -1345,20 +1345,56 @@ gst_audio_resample_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstAudioResample *resample;
gint quality;
resample = GST_AUDIO_RESAMPLE (object);
switch (prop_id) {
case PROP_QUALITY:
GST_BASE_TRANSFORM_LOCK (resample);
resample->quality = g_value_get_int (value);
GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality);
quality = g_value_get_int (value);
GST_DEBUG_OBJECT (resample, "new quality %d", quality);
gst_audio_resample_update_state (resample, resample->width,
resample->channels, resample->inrate, resample->outrate,
resample->quality, resample->fp);
quality, resample->fp);
GST_BASE_TRANSFORM_UNLOCK (resample);
break;
case PROP_FILTER_LENGTH:{
gint filter_length = g_value_get_int (value);
GST_BASE_TRANSFORM_LOCK (resample);
if (filter_length <= 8)
quality = 0;
else if (filter_length <= 16)
quality = 1;
else if (filter_length <= 32)
quality = 2;
else if (filter_length <= 48)
quality = 3;
else if (filter_length <= 64)
quality = 4;
else if (filter_length <= 80)
quality = 5;
else if (filter_length <= 96)
quality = 6;
else if (filter_length <= 128)
quality = 7;
else if (filter_length <= 160)
quality = 8;
else if (filter_length <= 192)
quality = 9;
else
quality = 10;
GST_DEBUG_OBJECT (resample, "new quality %d", quality);
gst_audio_resample_update_state (resample, resample->width,
resample->channels, resample->inrate, resample->outrate,
quality, resample->fp);
GST_BASE_TRANSFORM_UNLOCK (resample);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;

View file

@ -64,10 +64,30 @@
#ifdef OUTSIDE_SPEEX
#include <stdlib.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <glib.h>
#ifdef HAVE_ORC
#include <orc/orc.h>
#endif
#define EXPORT G_GNUC_INTERNAL
#ifdef _USE_SSE
#ifndef HAVE_XMMINTRIN_H
#undef _USE_SSE
#endif
#endif
#ifdef _USE_SSE2
#ifndef HAVE_EMMINTRIN_H
#undef _USE_SSE2
#endif
#endif
static inline void *
speex_alloc (int size)
{
@ -110,7 +130,7 @@ speex_free (void *ptr)
#define NULL 0
#endif
#ifdef _USE_SSE
#if defined _USE_SSE || defined _USE_SSE2
#include "resample_sse.h"
#endif
@ -121,6 +141,28 @@ speex_free (void *ptr)
#define FIXED_STACK_ALLOC 1024
#endif
/* Allow selecting SSE or not when compiled with SSE support */
#ifdef _USE_SSE
#define SSE_FALLBACK(macro) \
if (st->use_sse) goto sse_##macro##_sse; {
#define SSE_IMPLEMENTATION(macro) \
goto sse_##macro##_end; } sse_##macro##_sse: {
#define SSE_END(macro) sse_##macro##_end:; }
#else
#define SSE_FALLBACK(macro)
#endif
#ifdef _USE_SSE2
#define SSE2_FALLBACK(macro) \
if (st->use_sse2) goto sse2_##macro##_sse2; {
#define SSE2_IMPLEMENTATION(macro) \
goto sse2_##macro##_end; } sse2_##macro##_sse2: {
#define SSE2_END(macro) sse2_##macro##_end:; }
#else
#define SSE2_FALLBACK(macro)
#endif
typedef int (*resampler_basic_func) (SpeexResamplerState *, spx_uint32_t,
const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *);
@ -155,6 +197,9 @@ struct SpeexResamplerState_
int in_stride;
int out_stride;
int use_sse:1;
int use_sse2:1;
};
static double kaiser12_table[68] = {
@ -410,8 +455,8 @@ resampler_basic_direct_single (SpeexResamplerState * st,
const spx_word16_t *sinc = &sinc_table[samp_frac_num * N];
const spx_word16_t *iptr = &in[last_sample];
#ifndef OVERRIDE_INNER_PRODUCT_SINGLE
sum = 0;
SSE_FALLBACK (INNER_PRODUCT_SINGLE)
sum = 0;
for (j = 0; j < N; j++)
sum += MULT16_16 (sinc[j], iptr[j]);
@ -427,11 +472,12 @@ resampler_basic_direct_single (SpeexResamplerState * st,
}
sum = accum[0] + accum[1] + accum[2] + accum[3];
*/
#else
sum = inner_product_single (sinc, iptr, N);
#ifdef OVERRIDE_INNER_PRODUCT_SINGLE
SSE_IMPLEMENTATION (INNER_PRODUCT_SINGLE)
sum = inner_product_single (sinc, iptr, N);
SSE_END (INNER_PRODUCT_SINGLE)
#endif
out[out_stride * out_sample++] = SATURATE32 (PSHR32 (sum, 15), 32767);
out[out_stride * out_sample++] = SATURATE32 (PSHR32 (sum, 15), 32767);
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate) {
@ -471,7 +517,7 @@ resampler_basic_direct_double (SpeexResamplerState * st,
const spx_word16_t *sinc = &sinc_table[samp_frac_num * N];
const spx_word16_t *iptr = &in[last_sample];
#ifndef OVERRIDE_INNER_PRODUCT_DOUBLE
SSE2_FALLBACK (INNER_PRODUCT_DOUBLE)
double accum[4] = { 0, 0, 0, 0 };
for (j = 0; j < N; j += 4) {
@ -481,11 +527,12 @@ resampler_basic_direct_double (SpeexResamplerState * st,
accum[3] += sinc[j + 3] * iptr[j + 3];
}
sum = accum[0] + accum[1] + accum[2] + accum[3];
#else
sum = inner_product_double (sinc, iptr, N);
#ifdef OVERRIDE_INNER_PRODUCT_DOUBLE
SSE2_IMPLEMENTATION (INNER_PRODUCT_DOUBLE)
sum = inner_product_double (sinc, iptr, N);
SSE2_END (INNER_PRODUCT_DOUBLE)
#endif
out[out_stride * out_sample++] = PSHR32 (sum, 15);
out[out_stride * out_sample++] = PSHR32 (sum, 15);
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate) {
@ -534,7 +581,7 @@ resampler_basic_interpolate_single (SpeexResamplerState * st,
spx_word16_t interp[4];
#ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
SSE_FALLBACK (INTERPOLATE_PRODUCT_SINGLE)
spx_word32_t accum[4] = { 0, 0, 0, 0 };
for (j = 0; j < N; j++) {
@ -559,15 +606,16 @@ resampler_basic_interpolate_single (SpeexResamplerState * st,
1)) + MULT16_32_Q15 (interp[1], SHR32 (accum[1],
1)) + MULT16_32_Q15 (interp[2], SHR32 (accum[2],
1)) + MULT16_32_Q15 (interp[3], SHR32 (accum[3], 1));
#else
cubic_coef (frac, interp);
#ifdef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
SSE_IMPLEMENTATION (INTERPOLATE_PRODUCT_SINGLE)
cubic_coef (frac, interp);
sum =
interpolate_product_single (iptr,
st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample,
interp);
SSE_END (INTERPOLATE_PRODUCT_SINGLE)
#endif
out[out_stride * out_sample++] = SATURATE32 (PSHR32 (sum, 14), 32767);
out[out_stride * out_sample++] = SATURATE32 (PSHR32 (sum, 14), 32767);
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate) {
@ -624,7 +672,7 @@ resampler_basic_interpolate_double (SpeexResamplerState * st,
spx_word16_t interp[4];
#ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
SSE2_FALLBACK (INTERPOLATE_PRODUCT_DOUBLE)
double accum[4] = { 0, 0, 0, 0 };
for (j = 0; j < N; j++) {
@ -648,15 +696,16 @@ resampler_basic_interpolate_double (SpeexResamplerState * st,
MULT16_32_Q15 (interp[0], accum[0]) + MULT16_32_Q15 (interp[1],
accum[1]) + MULT16_32_Q15 (interp[2],
accum[2]) + MULT16_32_Q15 (interp[3], accum[3]);
#else
cubic_coef (frac, interp);
#ifdef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
SSE2_IMPLEMENTATION (INTERPOLATE_PRODUCT_DOUBLE)
cubic_coef (frac, interp);
sum =
interpolate_product_double (iptr,
st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample,
interp);
SSE2_END (INTERPOLATE_PRODUCT_DOUBLE)
#endif
out[out_stride * out_sample++] = PSHR32 (sum, 15);
out[out_stride * out_sample++] = PSHR32 (sum, 15);
last_sample += int_advance;
samp_frac_num += frac_advance;
if (samp_frac_num >= den_rate) {
@ -875,6 +924,17 @@ speex_resampler_init (spx_uint32_t nb_channels, spx_uint32_t in_rate,
out_rate, quality, err);
}
static void
check_insn_set (SpeexResamplerState * st, const char *name)
{
if (!name)
return;
if (!strcmp (name, "sse"))
st->use_sse = 1;
if (!strcmp (name, "sse2"))
st->use_sse = st->use_sse2 = 1;
}
EXPORT SpeexResamplerState *
speex_resampler_init_frac (spx_uint32_t nb_channels, spx_uint32_t ratio_num,
spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate,
@ -912,6 +972,23 @@ speex_resampler_init_frac (spx_uint32_t nb_channels, spx_uint32_t ratio_num,
st->buffer_size = 160;
#endif
st->use_sse = st->use_sse2 = 0;
#if defined HAVE_ORC && !defined DISABLE_ORC
orc_init ();
{
OrcTarget *target = orc_target_get_default ();
if (target) {
unsigned int flags = orc_target_get_default_flags (target);
check_insn_set (st, orc_target_get_name (target));
for (i = 0; i < 32; ++i) {
if (flags & (1 << i)) {
check_insn_set (st, orc_target_get_flag_name (target, i));
}
}
}
}
#endif
/* Per channel data */
st->last_sample = (spx_int32_t *) speex_alloc (nb_channels * sizeof (int));
st->magic_samples = (spx_uint32_t *) speex_alloc (nb_channels * sizeof (int));

View file

@ -34,7 +34,9 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_XMMINTRIN_H
#include <xmmintrin.h>
#endif
#define OVERRIDE_INNER_PRODUCT_SINGLE
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
@ -72,9 +74,27 @@ static inline float interpolate_product_single(const float *a, const float *b, u
}
#ifdef _USE_SSE2
#ifdef HAVE_EMMINTRIN_H
#include <emmintrin.h>
#endif
#define OVERRIDE_INNER_PRODUCT_DOUBLE
#ifdef DOUBLE_PRECISION
static inline double inner_product_double(const double *a, const double *b, unsigned int len)
{
int i;
double ret;
__m128d sum = _mm_setzero_pd();
for (i=0;i<len;i+=4)
{
sum = _mm_add_pd(sum, _mm_mul_pd(_mm_loadu_pd(a+i), _mm_loadu_pd(b+i)));
sum = _mm_add_pd(sum, _mm_mul_pd(_mm_loadu_pd(a+i+2), _mm_loadu_pd(b+i+2)));
}
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
_mm_store_sd(&ret, sum);
return ret;
}
#else
static inline double inner_product_double(const float *a, const float *b, unsigned int len)
{
int i;
@ -95,8 +115,39 @@ static inline double inner_product_double(const float *a, const float *b, unsign
_mm_store_sd(&ret, sum);
return ret;
}
#endif
#define OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
#ifdef DOUBLE_PRECISION
static inline double interpolate_product_double(const double *a, const double *b, unsigned int len, const spx_uint32_t oversample, double *frac) {
int i;
double ret;
__m128d sum;
__m128d sum1 = _mm_setzero_pd();
__m128d sum2 = _mm_setzero_pd();
__m128d f1 = _mm_loadu_pd(frac);
__m128d f2 = _mm_loadu_pd(frac+2);
__m128d t;
for(i=0;i<len;i+=2)
{
t = _mm_mul_pd(_mm_load1_pd(a+i), _mm_loadu_pd(b+i*oversample));
sum1 = _mm_add_pd(sum1, t);
sum2 = _mm_add_pd(sum2, _mm_unpackhi_pd(t, t));
t = _mm_mul_pd(_mm_load1_pd(a+i+1), _mm_loadu_pd(b+(i+1)*oversample));
sum1 = _mm_add_pd(sum1, t);
sum2 = _mm_add_pd(sum2, _mm_unpackhi_pd(t, t));
}
sum1 = _mm_mul_pd(f1, sum1);
sum2 = _mm_mul_pd(f2, sum2);
sum = _mm_add_pd(sum1, sum2);
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
_mm_store_sd(&ret, sum);
return ret;
}
#else
static inline double interpolate_product_double(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
int i;
double ret;
@ -124,5 +175,6 @@ static inline double interpolate_product_double(const float *a, const float *b,
_mm_store_sd(&ret, sum);
return ret;
}
#endif
#endif

View file

@ -17,6 +17,7 @@
* Boston, MA 02111-1307, USA.
*/
#define _USE_SSE2
#define FLOATING_POINT
#define DOUBLE_PRECISION
#define OUTSIDE_SPEEX

View file

@ -17,6 +17,8 @@
* Boston, MA 02111-1307, USA.
*/
#define _USE_SSE
#define _USE_SSE2
#define FLOATING_POINT
#define OUTSIDE_SPEEX
#define RANDOM_PREFIX resample_float

View file

@ -120,8 +120,8 @@
typedef enum
{
GST_ENC_FLAG_NATIVE_AUDIO = (1 << 0),
GST_ENC_FLAG_NATIVE_VIDEO = (1 << 1)
GST_ENC_FLAG_NO_AUDIO_CONVERSION = (1 << 0),
GST_ENC_FLAG_NO_VIDEO_CONVERSION = (1 << 1)
} GstEncFlags;
#define GST_TYPE_ENC_FLAGS (gst_enc_flags_get_type())
@ -265,10 +265,10 @@ GType
gst_enc_flags_get_type (void)
{
static const GFlagsValue values[] = {
{C_FLAGS (GST_ENC_FLAG_NATIVE_AUDIO), "Only use native audio formats",
"native-audio"},
{C_FLAGS (GST_ENC_FLAG_NATIVE_VIDEO), "Only use native video formats",
"native-video"},
{C_FLAGS (GST_ENC_FLAG_NO_AUDIO_CONVERSION), "Do not use audio conversion "
"elements", "no-audio-conversion"},
{C_FLAGS (GST_ENC_FLAG_NO_VIDEO_CONVERSION), "Do not use video conversion "
"elements", "no-video-conversion"},
{0, NULL, NULL}
};
static volatile GType id = 0;
@ -346,13 +346,13 @@ gst_encode_bin_class_init (GstEncodeBinClass * klass)
"The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_klass, PROP_QUEUE_BUFFERS_MAX,
g_object_class_install_property (gobject_klass, PROP_QUEUE_BYTES_MAX,
g_param_spec_uint ("queue-bytes-max", "Max. size (kB)",
"Max. amount of data in the queue (bytes, 0=disable)",
0, G_MAXUINT, DEFAULT_QUEUE_BYTES_MAX,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_klass, PROP_QUEUE_BYTES_MAX,
g_object_class_install_property (gobject_klass, PROP_QUEUE_BUFFERS_MAX,
g_param_spec_uint ("queue-buffers-max", "Max. size (buffers)",
"Max. number of buffers in the queue (0=disable)", 0, G_MAXUINT,
DEFAULT_QUEUE_BUFFERS_MAX,
@ -1044,7 +1044,8 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
* in the input queue */
last = sgroup->outqueue = gst_element_factory_make ("queue", NULL);
g_object_set (sgroup->outqueue, "max-size-buffers", (guint32) 1,
"max-size-bytes", (guint32) 0, "max-size-time", (guint64) 0, NULL);
"max-size-bytes", (guint32) 0, "max-size-time", (guint64) 0,
"silent", TRUE, NULL);
gst_bin_add (GST_BIN (ebin), sgroup->outqueue);
tosync = g_list_append (tosync, sgroup->outqueue);
@ -1163,7 +1164,7 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
g_object_set (sgroup->inqueue, "max-size-buffers",
(guint32) ebin->queue_buffers_max, "max-size-bytes",
(guint32) ebin->queue_bytes_max, "max-size-time",
(guint64) ebin->queue_time_max, NULL);
(guint64) ebin->queue_time_max, "silent", TRUE, NULL);
gst_bin_add (GST_BIN (ebin), sgroup->inqueue);
tosync = g_list_append (tosync, sgroup->inqueue);
@ -1262,7 +1263,8 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
/* 3.2. restriction elements */
/* FIXME : Once we have properties for specific converters, use those */
if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
const gboolean native_video = !!(ebin->flags & GST_ENC_FLAG_NATIVE_VIDEO);
const gboolean native_video =
!!(ebin->flags & GST_ENC_FLAG_NO_VIDEO_CONVERSION);
GstElement *cspace = NULL, *scale, *vrate, *cspace2 = NULL;
GST_LOG ("Adding conversion elements for video stream");
@ -1324,7 +1326,7 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof,
}
} else if (GST_IS_ENCODING_AUDIO_PROFILE (sprof)
&& !(ebin->flags & GST_ENC_FLAG_NATIVE_AUDIO)) {
&& !(ebin->flags & GST_ENC_FLAG_NO_AUDIO_CONVERSION)) {
GstElement *aconv, *ares, *arate, *aconv2;
GST_LOG ("Adding conversion elements for audio stream");

2074
gst/playback/gstdecodebin.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -417,7 +417,7 @@ static void gst_decode_group_free (GstDecodeGroup * group);
static GstDecodeGroup *gst_decode_group_new (GstDecodeBin * dbin,
GstDecodeChain * chain);
static gboolean gst_decode_chain_is_complete (GstDecodeChain * chain);
static void gst_decode_chain_handle_eos (GstDecodeChain * chain);
static gboolean gst_decode_chain_handle_eos (GstDecodeChain * chain);
static gboolean gst_decode_chain_expose (GstDecodeChain * chain,
GList ** endpads, gboolean * missing_plugin);
static gboolean gst_decode_chain_is_drained (GstDecodeChain * chain);
@ -2865,14 +2865,14 @@ out:
/* check if the group is drained, meaning all pads have seen an EOS
* event. */
static void
static gboolean
gst_decode_pad_handle_eos (GstDecodePad * pad)
{
GstDecodeChain *chain = pad->chain;
GST_LOG_OBJECT (pad->dbin, "chain : %p, pad %p", chain, pad);
pad->drained = TRUE;
gst_decode_chain_handle_eos (chain);
return gst_decode_chain_handle_eos (chain);
}
/* gst_decode_chain_handle_eos:
@ -2883,17 +2883,23 @@ gst_decode_pad_handle_eos (GstDecodePad * pad)
* If there are groups to switch to, hide the current active
* one and expose the new one.
*
* If a group isn't completely drained (i.e. we received EOS
* only on one of the streams) this function will return FALSE
* to indicate the EOS on the given chain should be dropped
* to avoid it from going downstream.
*
* MT-safe, don't call with chain lock!
*/
static void
static gboolean
gst_decode_chain_handle_eos (GstDecodeChain * eos_chain)
{
GstDecodeBin *dbin = eos_chain->dbin;
GstDecodeGroup *group;
GstDecodeChain *chain = eos_chain;
gboolean drained;
gboolean forward_eos = TRUE;
g_return_if_fail (eos_chain->endpad);
g_return_val_if_fail (eos_chain->endpad, TRUE);
CHAIN_MUTEX_LOCK (chain);
while ((group = chain->parent)) {
@ -2913,6 +2919,8 @@ gst_decode_chain_handle_eos (GstDecodeChain * eos_chain)
/* Now either group == NULL and chain == dbin->decode_chain
* or chain is the lowest chain that has a non-drained group */
if (chain->active_group && drained && chain->next_groups) {
/* There's an active group which is drained and we have another
* one to switch to. */
GST_DEBUG_OBJECT (dbin, "Hiding current group %p", chain->active_group);
gst_decode_group_hide (chain->active_group);
chain->old_groups = g_list_prepend (chain->old_groups, chain->active_group);
@ -2927,6 +2935,7 @@ gst_decode_chain_handle_eos (GstDecodeChain * eos_chain)
gst_decode_bin_expose (dbin);
EXPOSE_UNLOCK (dbin);
} else if (!chain->active_group || drained) {
/* The group is drained and there isn't a future one */
g_assert (chain == dbin->decode_chain);
CHAIN_MUTEX_UNLOCK (chain);
@ -2937,7 +2946,12 @@ gst_decode_chain_handle_eos (GstDecodeChain * eos_chain)
CHAIN_MUTEX_UNLOCK (chain);
GST_DEBUG_OBJECT (dbin,
"Current active group in chain %p is not drained yet", chain);
/* Instruct caller to drop EOS event if we have future groups */
if (chain->next_groups)
forward_eos = FALSE;
}
return forward_eos;
}
/* gst_decode_group_is_drained:
@ -3440,19 +3454,26 @@ source_pad_event_probe (GstPad * pad, GstProbeType type, gpointer type_data,
{
GstEvent *event = type_data;
GstDecodePad *dpad = user_data;
gboolean res = TRUE;
GST_LOG_OBJECT (pad, "%s dpad:%p", GST_EVENT_TYPE_NAME (event), dpad);
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
GST_DEBUG_OBJECT (pad, "we received EOS");
/* Check if all pads are drained. If there is a next group to expose, we
* will remove the ghostpad of the current group first, which unlinks the
* peer and so drops the EOS. */
gst_decode_pad_handle_eos (dpad);
/* Check if all pads are drained.
* * If there is no next group, we will let the EOS go through.
* * If there is a next group but the current group isn't completely
* drained, we will drop the EOS event.
* * If there is a next group to expose and this was the last non-drained
* pad for that group, we will remove the ghostpad of the current group
* first, which unlinks the peer and so drops the EOS. */
res = gst_decode_pad_handle_eos (dpad);
}
/* never drop events */
return GST_PROBE_OK;
if (res)
return GST_PROBE_OK;
else
return GST_PROBE_DROP;
}
static void

View file

@ -2792,20 +2792,26 @@ no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
/* if we have custom sinks, configure them now */
GST_SOURCE_GROUP_LOCK (group);
GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT,
group->audio_sink);
gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
group->audio_sink);
if (group->audio_sink) {
GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT,
group->audio_sink);
gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
group->audio_sink);
}
GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT,
group->video_sink);
gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
group->video_sink);
if (group->video_sink) {
GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT,
group->video_sink);
gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
group->video_sink);
}
GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT,
playbin->text_sink);
gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT,
playbin->text_sink);
if (playbin->text_sink) {
GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT,
playbin->text_sink);
gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT,
playbin->text_sink);
}
GST_SOURCE_GROUP_UNLOCK (group);

View file

@ -227,6 +227,7 @@ gst_sub_parse_src_query (GstPad * pad, GstQuery * query)
ret = TRUE;
gst_query_set_position (query, GST_FORMAT_TIME, self->segment.position);
}
break;
}
case GST_QUERY_SEEKING:
{
@ -246,7 +247,6 @@ gst_sub_parse_src_query (GstPad * pad, GstQuery * query)
}
gst_query_set_seeking (query, fmt, seekable, seekable ? 0 : -1, -1);
break;
}
default:

View file

@ -1014,6 +1014,7 @@ mp3_type_find_at_offset (GstTypeFind * tf, guint64 start_off,
guint layer = 0, bitrate, samplerate, channels;
guint found = 0; /* number of valid headers found */
guint64 offset = skipped;
gboolean changed = FALSE;
while (found < GST_MP3_TYPEFIND_TRY_HEADERS) {
guint32 head;
@ -1064,6 +1065,8 @@ mp3_type_find_at_offset (GstTypeFind * tf, guint64 start_off,
* 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*/
if (prev_layer)
changed = TRUE;
prev_layer = layer;
prev_channels = channels;
prev_samplerate = samplerate;
@ -1094,6 +1097,8 @@ mp3_type_find_at_offset (GstTypeFind * tf, guint64 start_off,
probability = GST_TYPE_FIND_MINIMUM;
if (start_off > 0)
probability /= 2;
if (!changed)
probability = (probability + GST_TYPE_FIND_MAXIMUM) / 2;
GST_INFO
("audio/mpeg calculated %u = %u * %u / %u * (%u - %"

View file

@ -241,7 +241,7 @@ volume_choose_func (GstVolume * self)
case 8:
/* only clamp if the gain is greater than 1.0
*/
if (self->current_vol_i16 > VOLUME_UNITY_INT8) {
if (self->current_vol_i8 > VOLUME_UNITY_INT8) {
self->process = volume_process_int8_clamp;
} else {
self->process = volume_process_int8;

View file

@ -12,7 +12,8 @@ TESTS_ENVIRONMENT = \
$(REGISTRY_ENVIRONMENT) \
GST_PLUGIN_SYSTEM_PATH= \
GST_PLUGIN_PATH=$(top_builddir)/gst:$(top_builddir)/sys:$(top_builddir)/ext:$(GST_PLUGINS_DIR) \
GST_PLUGIN_LOADING_WHITELIST="gstreamer:gst-plugins-base@$(top_builddir)"
GST_PLUGIN_LOADING_WHITELIST="gstreamer:gst-plugins-base@$(top_builddir)" \
GST_TAG_LICENSE_TRANSLATIONS_DICT="$(top_srcdir)/gst-libs/gst/tag/license-translations.dict"
# ths core dumps of some machines have PIDs appended
@ -391,6 +392,7 @@ elements_audioresample_CFLAGS = \
$(AM_CFLAGS)
elements_audioresample_LDADD = \
$(top_builddir)/gst-libs/gst/fft/libgstfft-@GST_MAJORMINOR@.la \
$(top_builddir)/gst-libs/gst/audio/libgstaudio-@GST_MAJORMINOR@.la \
$(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la \
$(GST_BASE_LIBS) \

View file

@ -27,6 +27,12 @@
#include <gst/audio/audio.h>
#include <gst/fft/gstfft.h>
#include <gst/fft/gstffts16.h>
#include <gst/fft/gstffts32.h>
#include <gst/fft/gstfftf32.h>
#include <gst/fft/gstfftf64.h>
/* For ease of programming we use globals to keep refs for our floating
* src and sink pads we create; otherwise we always have to do get_pad,
* get_peer, and then remove references in every test function */
@ -44,8 +50,8 @@ static GstPad *mysrcpad, *mysinkpad;
"channels = (int) [ 1, MAX ], " \
"rate = (int) [ 1, MAX ], " \
"endianness = (int) BYTE_ORDER, " \
"width = (int) 16, " \
"depth = (int) 16, " \
"width = (int) { 16, 32 }, " \
"depth = (int) { 16, 32 }, " \
"signed = (bool) TRUE"
#define RESAMPLE_CAPS_TEMPLATE_STRING \
@ -915,6 +921,190 @@ GST_START_TEST (test_timestamp_drift)
} GST_END_TEST;
#define FFT_HELPERS(type,ffttag,ffttag2,scale); \
static gdouble magnitude##ffttag (const GstFFT##ffttag##Complex *c) \
{ \
gdouble mag = (gdouble) c->r * (gdouble) c->r; \
mag += (gdouble) c->i * (gdouble) c->i; \
mag /= scale * scale; \
mag = 10.0 * log10 (mag); \
return mag; \
} \
static gdouble find_main_frequency_spot_##ffttag (const GstFFT##ffttag##Complex *v, \
int elements) \
{ \
int i; \
gdouble maxmag = -9999; \
int maxidx = 0; \
for (i=0; i<elements; ++i) { \
gdouble mag = magnitude##ffttag (v+i); \
if (mag > maxmag) { \
maxmag = mag; \
maxidx = i; \
} \
} \
return maxidx / (gdouble) elements; \
} \
static gboolean is_zero_except_##ffttag (const GstFFT##ffttag##Complex *v, int elements, \
gdouble spot) \
{ \
int i; \
for (i=0; i<elements; ++i) { \
gdouble pos = i / (gdouble) elements; \
gdouble mag = magnitude##ffttag (v+i); \
if (fabs (pos - spot) > 0.01) { \
if (mag > -55.0) { \
return FALSE; \
} \
} \
} \
return TRUE; \
} \
static void compare_ffts_##ffttag (const GstBuffer *inbuffer, const GstBuffer *outbuffer) \
{ \
int insamples = GST_BUFFER_SIZE (inbuffer) / sizeof(type) & ~1; \
int outsamples = GST_BUFFER_SIZE (outbuffer) / sizeof(type) & ~1; \
gdouble inspot, outspot; \
\
GstFFT##ffttag *inctx = gst_fft_##ffttag2##_new (insamples, FALSE); \
GstFFT##ffttag##Complex *in = g_new (GstFFT##ffttag##Complex, insamples / 2 + 1); \
GstFFT##ffttag *outctx = gst_fft_##ffttag2##_new (outsamples, FALSE); \
GstFFT##ffttag##Complex *out = g_new (GstFFT##ffttag##Complex, outsamples / 2 + 1); \
\
gst_fft_##ffttag2##_window (inctx, (type*)GST_BUFFER_DATA (inbuffer), \
GST_FFT_WINDOW_HAMMING); \
gst_fft_##ffttag2##_fft (inctx, (type*)GST_BUFFER_DATA (inbuffer), in); \
gst_fft_##ffttag2##_window (outctx, (type*)GST_BUFFER_DATA (outbuffer), \
GST_FFT_WINDOW_HAMMING); \
gst_fft_##ffttag2##_fft (outctx, (type*)GST_BUFFER_DATA (outbuffer), out); \
\
inspot = find_main_frequency_spot_##ffttag (in, insamples / 2 + 1); \
outspot = find_main_frequency_spot_##ffttag (out, outsamples / 2 + 1); \
GST_LOG ("Spots are %.3f and %.3f", inspot, outspot); \
fail_unless (fabs (outspot - inspot) < 0.05); \
fail_unless (is_zero_except_##ffttag (in, insamples / 2 + 1, inspot)); \
fail_unless (is_zero_except_##ffttag (out, outsamples / 2 + 1, outspot)); \
\
gst_fft_##ffttag2##_free (inctx); \
gst_fft_##ffttag2##_free (outctx); \
g_free (in); \
g_free (out); \
}
FFT_HELPERS (float, F32, f32, 2048.0f);
FFT_HELPERS (double, F64, f64, 2048.0);
FFT_HELPERS (gint16, S16, s16, 32767.0);
FFT_HELPERS (gint32, S32, s32, 2147483647.0);
#define FILL_BUFFER(type, desc, value); \
static void init_##type##_##desc (GstBuffer *buffer) \
{ \
type *ptr = (type *) GST_BUFFER_DATA (buffer); \
int i, nsamples = GST_BUFFER_SIZE (buffer) / sizeof (type); \
for (i = 0; i < nsamples; ++i) { \
*ptr++ = value; \
} \
}
FILL_BUFFER (float, silence, 0.0f);
FILL_BUFFER (double, silence, 0.0);
FILL_BUFFER (gint16, silence, 0);
FILL_BUFFER (gint32, silence, 0);
FILL_BUFFER (float, sine, sinf (i * 0.01f));
FILL_BUFFER (float, sine2, sinf (i * 1.8f));
FILL_BUFFER (double, sine, sin (i * 0.01));
FILL_BUFFER (double, sine2, sin (i * 1.8));
FILL_BUFFER (gint16, sine, (gint16) (32767 * sinf (i * 0.01f)));
FILL_BUFFER (gint16, sine2, (gint16) (32767 * sinf (i * 1.8f)));
FILL_BUFFER (gint32, sine, (gint32) (2147483647 * sinf (i * 0.01f)));
FILL_BUFFER (gint32, sine2, (gint32) (2147483647 * sinf (i * 1.8f)));
static void
run_fft_pipeline (int inrate, int outrate, int quality, int width, gboolean fp,
void (*init) (GstBuffer *),
void (*compare_ffts) (const GstBuffer *, const GstBuffer *))
{
GstElement *audioresample;
GstBuffer *inbuffer, *outbuffer;
GstCaps *caps;
const int nsamples = 2048;
audioresample = setup_audioresample (1, inrate, outrate, width, fp);
fail_unless (audioresample != NULL);
g_object_set (audioresample, "quality", quality, NULL);
caps = gst_pad_get_negotiated_caps (mysrcpad);
fail_unless (gst_caps_is_fixed (caps));
fail_unless (gst_element_set_state (audioresample,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
inbuffer = gst_buffer_new_and_alloc (nsamples * width / 8);
GST_BUFFER_DURATION (inbuffer) = GST_FRAMES_TO_CLOCK_TIME (nsamples, inrate);
GST_BUFFER_TIMESTAMP (inbuffer) = 0;
gst_buffer_set_caps (inbuffer, caps);
gst_buffer_ref (inbuffer);
(*init) (inbuffer);
/* pushing gives away my reference ... */
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
/* ... but it ends up being collected on the global buffer list */
fail_unless_equals_int (g_list_length (buffers), 1);
/* retrieve out buffer */
fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
fail_unless (gst_element_set_state (audioresample,
GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
(*compare_ffts) (inbuffer, outbuffer);
/* cleanup */
gst_buffer_unref (inbuffer);
cleanup_audioresample (audioresample);
}
GST_START_TEST (test_fft)
{
int quality;
size_t f0, f1;
static const int frequencies[] =
{ 8000, 16000, 44100, 48000, 128000, 12345, 54321 };
/* audioresample uses a mixed float/double code path for floats with quality>8, make sure we test it */
for (quality = 0; quality <= 10; quality += 5) {
for (f0 = 0; f0 < G_N_ELEMENTS (frequencies); ++f0) {
for (f1 = 0; f1 < G_N_ELEMENTS (frequencies); ++f1) {
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 32, TRUE,
&init_float_silence, &compare_ffts_F32);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 32, TRUE,
&init_float_sine, &compare_ffts_F32);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 32, TRUE,
&init_float_sine2, &compare_ffts_F32);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 64, TRUE,
&init_double_silence, &compare_ffts_F64);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 64, TRUE,
&init_double_sine, &compare_ffts_F64);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 64, TRUE,
&init_double_sine2, &compare_ffts_F64);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 16, FALSE,
&init_gint16_silence, &compare_ffts_S16);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 16, FALSE,
&init_gint16_sine, &compare_ffts_S16);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 16, FALSE,
&init_gint16_sine2, &compare_ffts_S16);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 32, FALSE,
&init_gint32_silence, &compare_ffts_S32);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 32, FALSE,
&init_gint32_sine, &compare_ffts_S32);
run_fft_pipeline (frequencies[f0], frequencies[f0], quality, 32, FALSE,
&init_gint32_sine2, &compare_ffts_S32);
}
}
}
}
GST_END_TEST;
static Suite *
audioresample_suite (void)
{
@ -928,6 +1118,7 @@ audioresample_suite (void)
tcase_add_test (tc_chain, test_shutdown);
tcase_add_test (tc_chain, test_live_switch);
tcase_add_test (tc_chain, test_timestamp_drift);
tcase_add_test (tc_chain, test_fft);
#ifndef GST_DISABLE_PARSE
tcase_set_timeout (tc_chain, 360);

View file

@ -1,6 +1,7 @@
/* GStreamer unit tests for decodebin
*
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* Copyright (C) 2011 Hewlett-Packard Development Company, L.P.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -23,6 +24,7 @@
#endif
#include <gst/check/gstcheck.h>
#include <gst/base/gstbaseparse.h>
#include <unistd.h>
static const gchar dummytext[] =
@ -196,6 +198,177 @@ GST_START_TEST (test_reuse_without_decoders)
GST_END_TEST;
/* Fake mp3 parser for test */
typedef GstBaseParse TestMpegAudioParse;
typedef GstBaseParseClass TestMpegAudioParseClass;
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, layer=[1,3], parsed=(b)true")
);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, parsed=(bool) { false, true }")
);
static GType test_mpeg_audio_parse_get_type (void);
static gboolean test_mpeg_audio_parse_start (GstBaseParse * parse);
static gboolean test_mpeg_audio_parse_stop (GstBaseParse * parse);
static gboolean test_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, guint * size, gint * skipsize);
static GstFlowReturn test_mpeg_audio_parse_parse_frame (GstBaseParse * parse,
GstBaseParseFrame * frame);
GST_BOILERPLATE (TestMpegAudioParse, test_mpeg_audio_parse, GstBaseParse,
GST_TYPE_BASE_PARSE);
static void
test_mpeg_audio_parse_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class, "MPEG1 Audio Parser",
"Codec/Parser/Audio", "Pretends to parse mpeg1 audio stream",
"Foo Bar <foo@bar.com>");
}
static void
test_mpeg_audio_parse_class_init (TestMpegAudioParseClass * klass)
{
GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
parse_class->start = test_mpeg_audio_parse_start;
parse_class->stop = test_mpeg_audio_parse_stop;
parse_class->check_valid_frame = test_mpeg_audio_parse_check_valid_frame;
parse_class->parse_frame = test_mpeg_audio_parse_parse_frame;
}
static gint num_parse_instances = 0;
static void
test_mpeg_audio_parse_init (TestMpegAudioParse * mp3parse,
TestMpegAudioParseClass * klass)
{
/* catch decodebin plugging parsers in a loop early */
fail_unless (++num_parse_instances < 10);
}
static gboolean
test_mpeg_audio_parse_start (GstBaseParse * parse)
{
gst_base_parse_set_min_frame_size (parse, 6);
return TRUE;
}
static gboolean
test_mpeg_audio_parse_stop (GstBaseParse * parse)
{
return TRUE;
}
static gboolean
test_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, guint * framesize, gint * skipsize)
{
const guint8 *data = GST_BUFFER_DATA (frame->buffer);
if ((GST_READ_UINT16_BE (data) & 0xffe0) == 0xffe0) {
/* this framesize is hard-coded for ../test.mp3 */
*framesize = 1045;
return TRUE;
} else {
*skipsize = 1;
return FALSE;
}
}
static GstFlowReturn
test_mpeg_audio_parse_parse_frame (GstBaseParse * parse,
GstBaseParseFrame * frame)
{
if (GST_BUFFER_OFFSET (frame->buffer) == 0) {
GstCaps *caps;
caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1,
"mpegaudioversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3,
"rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 2, NULL);
gst_buffer_set_caps (frame->buffer, caps);
gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
gst_caps_unref (caps);
}
return GST_FLOW_OK;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "testmpegaudioparse", GST_RANK_NONE,
test_mpeg_audio_parse_get_type ());
}
GST_START_TEST (test_mp3_parser_loop)
{
GstStateChangeReturn sret;
GstPluginFeature *feature;
GstMessage *msg;
GstElement *pipe, *src, *dec;
gchar *path;
num_parse_instances = 0;
gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
"fakemp3parse", "fakemp3parse", plugin_init, VERSION, "LGPL",
"gst-plugins-base", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
feature = gst_default_registry_find_feature ("testmpegaudioparse",
GST_TYPE_ELEMENT_FACTORY);
gst_plugin_feature_set_rank (feature, GST_RANK_PRIMARY + 100);
pipe = gst_pipeline_new (NULL);
src = gst_element_factory_make ("filesrc", NULL);
fail_unless (src != NULL);
path = g_build_filename (GST_TEST_FILES_PATH, "test.mp3", NULL);
g_object_set (src, "location", path, NULL);
g_free (path);
dec = gst_element_factory_make ("decodebin", NULL);
fail_unless (dec != NULL);
gst_bin_add_many (GST_BIN (pipe), src, dec, NULL);
gst_element_link_many (src, dec, NULL);
sret = gst_element_set_state (pipe, GST_STATE_PLAYING);
fail_unless_equals_int (sret, GST_STATE_CHANGE_ASYNC);
/* wait for unlinked error */
msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipe),
GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR);
gst_message_unref (msg);
gst_element_set_state (pipe, GST_STATE_NULL);
gst_object_unref (pipe);
/* make sure out parser got plugged at all though */
fail_unless_equals_int (num_parse_instances, 1);
/* don't want to interfere with any other of the other tests */
gst_plugin_feature_set_rank (feature, GST_RANK_NONE);
gst_object_unref (feature);
}
GST_END_TEST;
static Suite *
decodebin_suite (void)
{
@ -205,6 +378,7 @@ decodebin_suite (void)
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_text_plain_streams);
tcase_add_test (tc_chain, test_reuse_without_decoders);
tcase_add_test (tc_chain, test_mp3_parser_loop);
return s;
}

View file

@ -2,7 +2,7 @@
*
* unit tests for the tag support library
*
* Copyright (C) 2006-2009 Tim-Philipp Müller <tim centricular net>
* Copyright (C) 2006-2011 Tim-Philipp Müller <tim centricular net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -29,6 +29,7 @@
#include <gst/tag/tag.h>
#include <gst/base/gstbytewriter.h>
#include <string.h>
#include <locale.h>
GST_START_TEST (test_parse_extended_comment)
{
@ -588,6 +589,7 @@ GST_START_TEST (test_id3_tags)
const gchar *genre;
genre = gst_tag_id3_genre_get (i);
GST_LOG ("genre: %s", genre);
fail_unless (genre != NULL);
}
@ -761,6 +763,190 @@ GST_START_TEST (test_language_utils)
GST_END_TEST;
#define SPECIFIC_L "http://creativecommons.org/licenses/by-nc-sa/2.5/scotland/"
#define GENERIC_L "http://creativecommons.org/licenses/by/1.0/"
#define DERIVED_L "http://creativecommons.org/licenses/sampling+/1.0/tw/"
GST_START_TEST (test_license_utils)
{
GHashTable *ht;
GError *err = NULL;
gchar **liblicense_refs, **r;
gchar **lrefs, **l;
gchar *path, *data = NULL;
gsize data_len;
/* test jurisdiction-specific license */
fail_unless_equals_int (gst_tag_get_license_flags (SPECIFIC_L), 0x01010703);
fail_unless_equals_string (gst_tag_get_license_nick (SPECIFIC_L),
"CC BY-NC-SA 2.5 SCOTLAND");
fail_unless_equals_string (gst_tag_get_license_version (SPECIFIC_L), "2.5");
fail_unless_equals_string (gst_tag_get_license_jurisdiction (SPECIFIC_L),
"scotland");
g_setenv ("GST_TAG_LICENSE_TRANSLATIONS_LANG", "C", TRUE);
fail_unless_equals_string (gst_tag_get_license_title (SPECIFIC_L),
"Attribution-NonCommercial-ShareAlike");
fail_unless (gst_tag_get_license_description (SPECIFIC_L) == NULL);
/* test generic license */
fail_unless_equals_int (gst_tag_get_license_flags (GENERIC_L), 0x01000307);
fail_unless_equals_string (gst_tag_get_license_nick (GENERIC_L), "CC BY 1.0");
fail_unless_equals_string (gst_tag_get_license_version (GENERIC_L), "1.0");
fail_unless (gst_tag_get_license_jurisdiction (GENERIC_L) == NULL);
g_setenv ("GST_TAG_LICENSE_TRANSLATIONS_LANG", "C", TRUE);
fail_unless_equals_string (gst_tag_get_license_title (GENERIC_L),
"Attribution");
fail_unless_equals_string (gst_tag_get_license_description (GENERIC_L),
"You must attribute the work in the manner specified by the author or licensor.");
#ifdef ENABLE_NLS
g_setenv ("GST_TAG_LICENSE_TRANSLATIONS_LANG", "fr", TRUE);
fail_unless_equals_string (gst_tag_get_license_title (GENERIC_L),
"Paternité");
fail_unless_equals_string (gst_tag_get_license_description (GENERIC_L),
"L'offrant autorise les autres à reproduire, distribuer et communiquer cette création au public. En échange, les personnes qui acceptent ce contrat doivent citer le nom de l'auteur original.");
#endif
/* test derived (for a certain jurisdiction) license */
fail_unless_equals_int (gst_tag_get_license_flags (DERIVED_L), 0x0100030d);
fail_unless_equals_string (gst_tag_get_license_nick (DERIVED_L),
"CC SAMPLING+ 1.0 TW");
fail_unless_equals_string (gst_tag_get_license_version (DERIVED_L), "1.0");
fail_unless_equals_string (gst_tag_get_license_jurisdiction (DERIVED_L),
"tw");
g_setenv ("GST_TAG_LICENSE_TRANSLATIONS_LANG", "C", TRUE);
fail_unless_equals_string (gst_tag_get_license_title (DERIVED_L),
"Sampling Plus");
fail_unless_equals_string (gst_tag_get_license_description (GENERIC_L),
"You must attribute the work in the manner specified by the author or licensor.");
/* test all we know about */
lrefs = gst_tag_get_licenses ();
fail_unless (lrefs != NULL);
fail_unless (*lrefs != NULL);
GST_INFO ("%d licenses", g_strv_length (lrefs));
fail_unless (g_strv_length (lrefs) >= 376);
ht = g_hash_table_new (g_str_hash, g_str_equal);
for (l = lrefs; l != NULL && *l != NULL; ++l) {
const gchar *ref, *nick, *title, *desc G_GNUC_UNUSED;
ref = (const gchar *) *l;
nick = gst_tag_get_license_nick (ref);
title = gst_tag_get_license_title (ref);
desc = gst_tag_get_license_description (ref);
fail_unless (nick != NULL, "no nick for license '%s'", ref);
fail_unless (title != NULL, "no title for license '%s'", ref);
GST_LOG ("ref: %s [nick %s]", ref, (nick) ? nick : "none");
GST_TRACE (" %s : %s", title, (desc) ? desc : "(no description)");
/* make sure the list contains no duplicates */
fail_if (g_hash_table_lookup (ht, (gpointer) ref) != NULL);
g_hash_table_insert (ht, (gpointer) ref, (gpointer) "meep");
}
g_hash_table_destroy (ht);
/* trailing slash shouldn't make a difference */
fail_unless_equals_int (gst_tag_get_license_flags
("http://creativecommons.org/licenses/by-nd/1.0/"),
gst_tag_get_license_flags
("http://creativecommons.org/licenses/by-nd/1.0"));
fail_unless_equals_string (gst_tag_get_license_nick
("http://creativecommons.org/licenses/by-nd/1.0/"),
gst_tag_get_license_nick
("http://creativecommons.org/licenses/by-nd/1.0"));
fail_unless_equals_int (gst_tag_get_license_flags
("http://creativecommons.org/licenses/by-nd/2.5/ca/"),
gst_tag_get_license_flags
("http://creativecommons.org/licenses/by-nd/2.5/ca"));
fail_unless_equals_string (gst_tag_get_license_nick
("http://creativecommons.org/licenses/by-nd/2.5/ca/"),
gst_tag_get_license_nick
("http://creativecommons.org/licenses/by-nd/2.5/ca"));
/* unknown licenses */
fail_unless (gst_tag_get_license_nick
("http://creativecommons.org/licenses/by-nd/25/ca/") == NULL);
fail_unless (gst_tag_get_license_flags
("http://creativecommons.org/licenses/by-nd/25/ca") == 0);
fail_unless (gst_tag_get_license_jurisdiction
("http://creativecommons.org/licenses/by-nd/25/ca/") == NULL);
fail_unless (gst_tag_get_license_jurisdiction
("http://creativecommons.org/licenses/by-nd/25/ca") == NULL);
fail_unless (gst_tag_get_license_title
("http://creativecommons.org/licenses/by-nd/25/ca") == NULL);
fail_unless (gst_tag_get_license_jurisdiction
("http://creativecommons.org/licenses/by-nd/25/ca") == NULL);
/* unknown prefixes even */
fail_unless (gst_tag_get_license_nick
("http://copycats.org/licenses/by-nd/2.5/ca/") == NULL);
fail_unless (gst_tag_get_license_flags
("http://copycats.org/licenses/by-nd/2.5/ca") == 0);
fail_unless (gst_tag_get_license_jurisdiction
("http://copycats.org/licenses/by-nd/2.5/ca/") == NULL);
fail_unless (gst_tag_get_license_title
("http://copycats.org/licenses/by-nd/2.5/ca/") == NULL);
fail_unless (gst_tag_get_license_description
("http://copycats.org/licenses/by-nd/2.5/ca/") == NULL);
/* read list of liblicense refs from file */
path = g_build_filename (GST_TEST_FILES_PATH, "license-uris", NULL);
GST_LOG ("reading file '%s'", path);
if (!g_file_get_contents (path, &data, &data_len, &err)) {
g_error ("error loading test file: %s", err->message);
}
while (data_len > 0 && data[data_len - 1] == '\n') {
data[--data_len] = '\0';
}
liblicense_refs = g_strsplit (data, "\n", -1);
g_free (data);
g_free (path);
fail_unless (g_strv_length (lrefs) >= g_strv_length (liblicense_refs));
for (r = liblicense_refs; r != NULL && *r != NULL; ++r) {
GstTagLicenseFlags flags;
const gchar *version, *nick, *jur;
const gchar *ref = *r;
GST_LOG ("liblicense ref: %s", ref);
version = gst_tag_get_license_version (ref);
if (strstr (ref, "publicdomain") != NULL)
fail_unless (version == NULL);
else
fail_unless (version != NULL, "expected version for license %s", ref);
flags = gst_tag_get_license_flags (ref);
fail_unless (flags != 0, "expected non-zero flags for license %s", ref);
nick = gst_tag_get_license_nick (ref);
fail_unless (nick != NULL, "expected nick for license %s", ref);
jur = gst_tag_get_license_jurisdiction (ref);
if (g_str_has_suffix (ref, "de/")) {
fail_unless_equals_string (jur, "de");
} else if (g_str_has_suffix (ref, "scotland")) {
fail_unless_equals_string (jur, "scotland");
} else if (g_str_has_suffix (ref, ".0") || g_str_has_suffix (ref, ".1")) {
fail_unless (jur == NULL);
}
}
g_strfreev (liblicense_refs);
g_strfreev (lrefs);
}
GST_END_TEST;
GST_START_TEST (test_xmp_formatting)
{
GstTagList *list;
@ -1234,7 +1420,6 @@ do_simple_exif_tag_serialization_deserialization (const gchar * gsttag,
GstTagList *taglist = gst_tag_list_new ();
gst_tag_list_add_value (taglist, GST_TAG_MERGE_REPLACE, gsttag, value);
do_exif_tag_serialization_deserialization (taglist);
gst_tag_list_free (taglist);
@ -1294,6 +1479,12 @@ GST_START_TEST (test_exif_tags_serialization_deserialization)
do_simple_exif_tag_serialization_deserialization (GST_TAG_APPLICATION_NAME,
&value);
/* non ascii chars */
g_value_set_static_string (&value, "AaÄäEeËëIiÏïOoÖöUuÜü");
do_simple_exif_tag_serialization_deserialization (GST_TAG_ARTIST, &value);
g_value_set_static_string (&value, "Äë");
do_simple_exif_tag_serialization_deserialization (GST_TAG_ARTIST, &value);
/* image orientation tests */
g_value_set_static_string (&value, "rotate-0");
do_simple_exif_tag_serialization_deserialization (GST_TAG_IMAGE_ORIENTATION,
@ -1649,6 +1840,7 @@ tag_suite (void)
tcase_add_test (tc_chain, test_id3_tags);
tcase_add_test (tc_chain, test_id3v1_utf8_tag);
tcase_add_test (tc_chain, test_language_utils);
tcase_add_test (tc_chain, test_license_utils);
tcase_add_test (tc_chain, test_xmp_formatting);
tcase_add_test (tc_chain, test_xmp_parsing);
tcase_add_test (tc_chain, test_xmp_tags_serialization_deserialization);

View file

@ -44,11 +44,15 @@ break_mainloop (gpointer data)
static gboolean
buffer_probe_cb (GstPad * pad, GstBuffer * buffer)
{
GstClockTime new_ts = GST_BUFFER_TIMESTAMP (buffer);
GST_LOG ("ts = %" GST_TIME_FORMAT, GST_TIME_ARGS (new_ts));
if (old_ts != GST_CLOCK_TIME_NONE) {
fail_unless (GST_BUFFER_TIMESTAMP (buffer) != old_ts,
"Two buffers had same timestamp");
fail_unless (new_ts != old_ts,
"Two buffers had same timestamp: %" GST_TIME_FORMAT,
GST_TIME_ARGS (old_ts));
}
old_ts = GST_BUFFER_TIMESTAMP (buffer);
old_ts = new_ts;
return TRUE;
}
@ -60,11 +64,6 @@ GST_START_TEST (test_basetime_calculation)
GstPad *pad;
GMainLoop *loop;
/* Don't run with osxaudiosrc . This is because libcheck runs the actual
* test in a forked process and causes havoc with osx's API. */
if (G_UNLIKELY (!g_ascii_strcasecmp (DEFAULT_AUDIOSRC, "osxaudiosrc")))
return;
loop = g_main_loop_new (NULL, FALSE);
/* The "main" pipeline */
@ -72,10 +71,9 @@ GST_START_TEST (test_basetime_calculation)
fail_if (p1 == NULL);
/* Create a sub-bin that is activated only in "certain situations" */
asrc = gst_element_factory_make (DEFAULT_AUDIOSRC, NULL);
asrc = gst_element_factory_make ("audiotestsrc", NULL);
if (asrc == NULL) {
GST_WARNING ("Cannot run test. test audio source %s not available",
DEFAULT_AUDIOSRC);
GST_WARNING ("Cannot run test. 'audiotestsrc' not available");
gst_element_set_state (p1, GST_STATE_NULL);
gst_object_unref (p1);
return;

View file

@ -1,4 +1,6 @@
EXTRA_DIST = \
623663.mts \
hls.m3u8 \
partialframe.mjpeg
license-uris \
partialframe.mjpeg \
test.mp3

376
tests/files/license-uris Normal file
View file

@ -0,0 +1,376 @@
http://creativecommons.org/licenses/by/1.0/fi/
http://creativecommons.org/licenses/by/1.0/il/
http://creativecommons.org/licenses/by/1.0/nl/
http://creativecommons.org/licenses/by/1.0/
http://creativecommons.org/licenses/by/2.0/at/
http://creativecommons.org/licenses/by/2.0/au/
http://creativecommons.org/licenses/by/2.0/be/
http://creativecommons.org/licenses/by/2.0/br/
http://creativecommons.org/licenses/by/2.0/ca/
http://creativecommons.org/licenses/by/2.0/cl/
http://creativecommons.org/licenses/by/2.0/de/
http://creativecommons.org/licenses/by/2.0/es/
http://creativecommons.org/licenses/by/2.0/fr/
http://creativecommons.org/licenses/by/2.0/hr/
http://creativecommons.org/licenses/by/2.0/it/
http://creativecommons.org/licenses/by/2.0/jp/
http://creativecommons.org/licenses/by/2.0/kr/
http://creativecommons.org/licenses/by/2.0/nl/
http://creativecommons.org/licenses/by/2.0/pl/
http://creativecommons.org/licenses/by/2.0/
http://creativecommons.org/licenses/by/2.0/tw/
http://creativecommons.org/licenses/by/2.0/uk/
http://creativecommons.org/licenses/by/2.0/za/
http://creativecommons.org/licenses/by/2.1/au/
http://creativecommons.org/licenses/by/2.1/es/
http://creativecommons.org/licenses/by/2.1/jp/
http://creativecommons.org/licenses/by/2.5/ar/
http://creativecommons.org/licenses/by/2.5/au/
http://creativecommons.org/licenses/by/2.5/bg/
http://creativecommons.org/licenses/by/2.5/br/
http://creativecommons.org/licenses/by/2.5/ca/
http://creativecommons.org/licenses/by/2.5/ch/
http://creativecommons.org/licenses/by/2.5/cn/
http://creativecommons.org/licenses/by/2.5/co/
http://creativecommons.org/licenses/by/2.5/dk/
http://creativecommons.org/licenses/by/2.5/es/
http://creativecommons.org/licenses/by/2.5/hr/
http://creativecommons.org/licenses/by/2.5/hu/
http://creativecommons.org/licenses/by/2.5/il/
http://creativecommons.org/licenses/by/2.5/in/
http://creativecommons.org/licenses/by/2.5/it/
http://creativecommons.org/licenses/by/2.5/mk/
http://creativecommons.org/licenses/by/2.5/mt/
http://creativecommons.org/licenses/by/2.5/mx/
http://creativecommons.org/licenses/by/2.5/my/
http://creativecommons.org/licenses/by/2.5/nl/
http://creativecommons.org/licenses/by/2.5/pe/
http://creativecommons.org/licenses/by/2.5/pl/
http://creativecommons.org/licenses/by/2.5/pt/
http://creativecommons.org/licenses/by/2.5/
http://creativecommons.org/licenses/by/2.5/scotland/
http://creativecommons.org/licenses/by/2.5/se/
http://creativecommons.org/licenses/by/2.5/si/
http://creativecommons.org/licenses/by/2.5/tw/
http://creativecommons.org/licenses/by/2.5/za/
http://creativecommons.org/licenses/by/3.0/
http://creativecommons.org/licenses/by/3.0/us/
http://creativecommons.org/licenses/by-nc/1.0/fi/
http://creativecommons.org/licenses/by-nc/1.0/il/
http://creativecommons.org/licenses/by-nc/1.0/nl/
http://creativecommons.org/licenses/by-nc/1.0/
http://creativecommons.org/licenses/by-nc/2.0/at/
http://creativecommons.org/licenses/by-nc/2.0/au/
http://creativecommons.org/licenses/by-nc/2.0/be/
http://creativecommons.org/licenses/by-nc/2.0/br/
http://creativecommons.org/licenses/by-nc/2.0/ca/
http://creativecommons.org/licenses/by-nc/2.0/cl/
http://creativecommons.org/licenses/by-nc/2.0/de/
http://creativecommons.org/licenses/by-nc/2.0/es/
http://creativecommons.org/licenses/by-nc/2.0/fr/
http://creativecommons.org/licenses/by-nc/2.0/hr/
http://creativecommons.org/licenses/by-nc/2.0/it/
http://creativecommons.org/licenses/by-nc/2.0/jp/
http://creativecommons.org/licenses/by-nc/2.0/kr/
http://creativecommons.org/licenses/by-nc/2.0/nl/
http://creativecommons.org/licenses/by-nc/2.0/pl/
http://creativecommons.org/licenses/by-nc/2.0/
http://creativecommons.org/licenses/by-nc/2.0/tw/
http://creativecommons.org/licenses/by-nc/2.0/uk/
http://creativecommons.org/licenses/by-nc/2.0/za/
http://creativecommons.org/licenses/by-nc/2.1/au/
http://creativecommons.org/licenses/by-nc/2.1/es/
http://creativecommons.org/licenses/by-nc/2.1/jp/
http://creativecommons.org/licenses/by-nc/2.5/ar/
http://creativecommons.org/licenses/by-nc/2.5/au/
http://creativecommons.org/licenses/by-nc/2.5/bg/
http://creativecommons.org/licenses/by-nc/2.5/br/
http://creativecommons.org/licenses/by-nc/2.5/ca/
http://creativecommons.org/licenses/by-nc/2.5/ch/
http://creativecommons.org/licenses/by-nc/2.5/cn/
http://creativecommons.org/licenses/by-nc/2.5/co/
http://creativecommons.org/licenses/by-nc/2.5/dk/
http://creativecommons.org/licenses/by-nc/2.5/es/
http://creativecommons.org/licenses/by-nc/2.5/hr/
http://creativecommons.org/licenses/by-nc/2.5/hu/
http://creativecommons.org/licenses/by-nc/2.5/il/
http://creativecommons.org/licenses/by-nc/2.5/in/
http://creativecommons.org/licenses/by-nc/2.5/it/
http://creativecommons.org/licenses/by-nc/2.5/mk/
http://creativecommons.org/licenses/by-nc/2.5/mt/
http://creativecommons.org/licenses/by-nc/2.5/mx/
http://creativecommons.org/licenses/by-nc/2.5/my/
http://creativecommons.org/licenses/by-nc/2.5/nl/
http://creativecommons.org/licenses/by-nc/2.5/pe/
http://creativecommons.org/licenses/by-nc/2.5/pl/
http://creativecommons.org/licenses/by-nc/2.5/pt/
http://creativecommons.org/licenses/by-nc/2.5/
http://creativecommons.org/licenses/by-nc/2.5/scotland/
http://creativecommons.org/licenses/by-nc/2.5/se/
http://creativecommons.org/licenses/by-nc/2.5/si/
http://creativecommons.org/licenses/by-nc/2.5/tw/
http://creativecommons.org/licenses/by-nc/2.5/za/
http://creativecommons.org/licenses/by-nc/3.0/
http://creativecommons.org/licenses/by-nc/3.0/us/
http://creativecommons.org/licenses/by-nc-nd/2.0/at/
http://creativecommons.org/licenses/by-nc-nd/2.0/au/
http://creativecommons.org/licenses/by-nc-nd/2.0/be/
http://creativecommons.org/licenses/by-nc-nd/2.0/br/
http://creativecommons.org/licenses/by-nc-nd/2.0/ca/
http://creativecommons.org/licenses/by-nc-nd/2.0/cl/
http://creativecommons.org/licenses/by-nc-nd/2.0/de/
http://creativecommons.org/licenses/by-nc-nd/2.0/es/
http://creativecommons.org/licenses/by-nc-nd/2.0/fr/
http://creativecommons.org/licenses/by-nc-nd/2.0/hr/
http://creativecommons.org/licenses/by-nc-nd/2.0/it/
http://creativecommons.org/licenses/by-nc-nd/2.0/jp/
http://creativecommons.org/licenses/by-nc-nd/2.0/kr/
http://creativecommons.org/licenses/by-nc-nd/2.0/nl/
http://creativecommons.org/licenses/by-nc-nd/2.0/pl/
http://creativecommons.org/licenses/by-nc-nd/2.0/
http://creativecommons.org/licenses/by-nc-nd/2.0/tw/
http://creativecommons.org/licenses/by-nc-nd/2.0/uk/
http://creativecommons.org/licenses/by-nc-nd/2.0/za/
http://creativecommons.org/licenses/by-nc-nd/2.1/au/
http://creativecommons.org/licenses/by-nc-nd/2.1/es/
http://creativecommons.org/licenses/by-nc-nd/2.1/jp/
http://creativecommons.org/licenses/by-nc-nd/2.5/ar/
http://creativecommons.org/licenses/by-nc-nd/2.5/au/
http://creativecommons.org/licenses/by-nc-nd/2.5/bg/
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
http://creativecommons.org/licenses/by-nc-nd/2.5/ca/
http://creativecommons.org/licenses/by-nc-nd/2.5/ch/
http://creativecommons.org/licenses/by-nc-nd/2.5/cn/
http://creativecommons.org/licenses/by-nc-nd/2.5/co/
http://creativecommons.org/licenses/by-nc-nd/2.5/dk/
http://creativecommons.org/licenses/by-nc-nd/2.5/es/
http://creativecommons.org/licenses/by-nc-nd/2.5/hr/
http://creativecommons.org/licenses/by-nc-nd/2.5/hu/
http://creativecommons.org/licenses/by-nc-nd/2.5/il/
http://creativecommons.org/licenses/by-nc-nd/2.5/in/
http://creativecommons.org/licenses/by-nc-nd/2.5/it/
http://creativecommons.org/licenses/by-nc-nd/2.5/mk/
http://creativecommons.org/licenses/by-nc-nd/2.5/mt/
http://creativecommons.org/licenses/by-nc-nd/2.5/mx/
http://creativecommons.org/licenses/by-nc-nd/2.5/my/
http://creativecommons.org/licenses/by-nc-nd/2.5/nl/
http://creativecommons.org/licenses/by-nc-nd/2.5/pe/
http://creativecommons.org/licenses/by-nc-nd/2.5/pl/
http://creativecommons.org/licenses/by-nc-nd/2.5/pt/
http://creativecommons.org/licenses/by-nc-nd/2.5/
http://creativecommons.org/licenses/by-nc-nd/2.5/scotland/
http://creativecommons.org/licenses/by-nc-nd/2.5/se/
http://creativecommons.org/licenses/by-nc-nd/2.5/si/
http://creativecommons.org/licenses/by-nc-nd/2.5/tw/
http://creativecommons.org/licenses/by-nc-nd/2.5/za/
http://creativecommons.org/licenses/by-nc-nd/3.0/
http://creativecommons.org/licenses/by-nc-nd/3.0/us/
http://creativecommons.org/licenses/by-nc-sa/1.0/fi/
http://creativecommons.org/licenses/by-nc-sa/1.0/il/
http://creativecommons.org/licenses/by-nc-sa/1.0/nl/
http://creativecommons.org/licenses/by-nc-sa/1.0/
http://creativecommons.org/licenses/by-nc-sa/2.0/at/
http://creativecommons.org/licenses/by-nc-sa/2.0/au/
http://creativecommons.org/licenses/by-nc-sa/2.0/be/
http://creativecommons.org/licenses/by-nc-sa/2.0/br/
http://creativecommons.org/licenses/by-nc-sa/2.0/ca/
http://creativecommons.org/licenses/by-nc-sa/2.0/cl/
http://creativecommons.org/licenses/by-nc-sa/2.0/de/
http://creativecommons.org/licenses/by-nc-sa/2.0/es/
http://creativecommons.org/licenses/by-nc-sa/2.0/fr/
http://creativecommons.org/licenses/by-nc-sa/2.0/hr/
http://creativecommons.org/licenses/by-nc-sa/2.0/it/
http://creativecommons.org/licenses/by-nc-sa/2.0/jp/
http://creativecommons.org/licenses/by-nc-sa/2.0/kr/
http://creativecommons.org/licenses/by-nc-sa/2.0/nl/
http://creativecommons.org/licenses/by-nc-sa/2.0/pl/
http://creativecommons.org/licenses/by-nc-sa/2.0/
http://creativecommons.org/licenses/by-nc-sa/2.0/tw/
http://creativecommons.org/licenses/by-nc-sa/2.0/uk/
http://creativecommons.org/licenses/by-nc-sa/2.0/za/
http://creativecommons.org/licenses/by-nc-sa/2.1/au/
http://creativecommons.org/licenses/by-nc-sa/2.1/es/
http://creativecommons.org/licenses/by-nc-sa/2.1/jp/
http://creativecommons.org/licenses/by-nc-sa/2.5/ar/
http://creativecommons.org/licenses/by-nc-sa/2.5/au/
http://creativecommons.org/licenses/by-nc-sa/2.5/bg/
http://creativecommons.org/licenses/by-nc-sa/2.5/br/
http://creativecommons.org/licenses/by-nc-sa/2.5/ca/
http://creativecommons.org/licenses/by-nc-sa/2.5/ch/
http://creativecommons.org/licenses/by-nc-sa/2.5/cn/
http://creativecommons.org/licenses/by-nc-sa/2.5/co/
http://creativecommons.org/licenses/by-nc-sa/2.5/dk/
http://creativecommons.org/licenses/by-nc-sa/2.5/es/
http://creativecommons.org/licenses/by-nc-sa/2.5/hr/
http://creativecommons.org/licenses/by-nc-sa/2.5/hu/
http://creativecommons.org/licenses/by-nc-sa/2.5/il/
http://creativecommons.org/licenses/by-nc-sa/2.5/in/
http://creativecommons.org/licenses/by-nc-sa/2.5/it/
http://creativecommons.org/licenses/by-nc-sa/2.5/mk/
http://creativecommons.org/licenses/by-nc-sa/2.5/mt/
http://creativecommons.org/licenses/by-nc-sa/2.5/mx/
http://creativecommons.org/licenses/by-nc-sa/2.5/my/
http://creativecommons.org/licenses/by-nc-sa/2.5/nl/
http://creativecommons.org/licenses/by-nc-sa/2.5/pe/
http://creativecommons.org/licenses/by-nc-sa/2.5/pl/
http://creativecommons.org/licenses/by-nc-sa/2.5/pt/
http://creativecommons.org/licenses/by-nc-sa/2.5/
http://creativecommons.org/licenses/by-nc-sa/2.5/scotland/
http://creativecommons.org/licenses/by-nc-sa/2.5/se/
http://creativecommons.org/licenses/by-nc-sa/2.5/si/
http://creativecommons.org/licenses/by-nc-sa/2.5/tw/
http://creativecommons.org/licenses/by-nc-sa/2.5/za/
http://creativecommons.org/licenses/by-nc-sa/3.0/
http://creativecommons.org/licenses/by-nc-sa/3.0/us/
http://creativecommons.org/licenses/by-nd/1.0/fi/
http://creativecommons.org/licenses/by-nd/1.0/il/
http://creativecommons.org/licenses/by-nd/1.0/nl/
http://creativecommons.org/licenses/by-nd/1.0/
http://creativecommons.org/licenses/by-nd/2.0/at/
http://creativecommons.org/licenses/by-nd/2.0/au/
http://creativecommons.org/licenses/by-nd/2.0/be/
http://creativecommons.org/licenses/by-nd/2.0/br/
http://creativecommons.org/licenses/by-nd/2.0/ca/
http://creativecommons.org/licenses/by-nd/2.0/cl/
http://creativecommons.org/licenses/by-nd/2.0/de/
http://creativecommons.org/licenses/by-nd/2.0/es/
http://creativecommons.org/licenses/by-nd/2.0/fr/
http://creativecommons.org/licenses/by-nd/2.0/hr/
http://creativecommons.org/licenses/by-nd/2.0/it/
http://creativecommons.org/licenses/by-nd/2.0/jp/
http://creativecommons.org/licenses/by-nd/2.0/kr/
http://creativecommons.org/licenses/by-nd/2.0/nl/
http://creativecommons.org/licenses/by-nd/2.0/pl/
http://creativecommons.org/licenses/by-nd/2.0/
http://creativecommons.org/licenses/by-nd/2.0/tw/
http://creativecommons.org/licenses/by-nd/2.0/uk/
http://creativecommons.org/licenses/by-nd/2.0/za/
http://creativecommons.org/licenses/by-nd/2.1/au/
http://creativecommons.org/licenses/by-nd/2.1/es/
http://creativecommons.org/licenses/by-nd/2.1/jp/
http://creativecommons.org/licenses/by-nd/2.5/ar/
http://creativecommons.org/licenses/by-nd/2.5/au/
http://creativecommons.org/licenses/by-nd/2.5/bg/
http://creativecommons.org/licenses/by-nd/2.5/br/
http://creativecommons.org/licenses/by-nd/2.5/ca/
http://creativecommons.org/licenses/by-nd/2.5/ch/
http://creativecommons.org/licenses/by-nd/2.5/cn/
http://creativecommons.org/licenses/by-nd/2.5/co/
http://creativecommons.org/licenses/by-nd/2.5/dk/
http://creativecommons.org/licenses/by-nd/2.5/es/
http://creativecommons.org/licenses/by-nd/2.5/hr/
http://creativecommons.org/licenses/by-nd/2.5/hu/
http://creativecommons.org/licenses/by-nd/2.5/il/
http://creativecommons.org/licenses/by-nd/2.5/in/
http://creativecommons.org/licenses/by-nd/2.5/it/
http://creativecommons.org/licenses/by-nd/2.5/mk/
http://creativecommons.org/licenses/by-nd/2.5/mt/
http://creativecommons.org/licenses/by-nd/2.5/mx/
http://creativecommons.org/licenses/by-nd/2.5/my/
http://creativecommons.org/licenses/by-nd/2.5/nl/
http://creativecommons.org/licenses/by-nd/2.5/pe/
http://creativecommons.org/licenses/by-nd/2.5/pl/
http://creativecommons.org/licenses/by-nd/2.5/pt/
http://creativecommons.org/licenses/by-nd/2.5/
http://creativecommons.org/licenses/by-nd/2.5/scotland/
http://creativecommons.org/licenses/by-nd/2.5/se/
http://creativecommons.org/licenses/by-nd/2.5/si/
http://creativecommons.org/licenses/by-nd/2.5/tw/
http://creativecommons.org/licenses/by-nd/2.5/za/
http://creativecommons.org/licenses/by-nd/3.0/
http://creativecommons.org/licenses/by-nd/3.0/us/
http://creativecommons.org/licenses/by-nd-nc/1.0/fi/
http://creativecommons.org/licenses/by-nd-nc/1.0/il/
http://creativecommons.org/licenses/by-nd-nc/1.0/nl/
http://creativecommons.org/licenses/by-nd-nc/1.0/
http://creativecommons.org/licenses/by-nd-nc/2.0/jp/
http://creativecommons.org/licenses/by-sa/1.0/fi/
http://creativecommons.org/licenses/by-sa/1.0/il/
http://creativecommons.org/licenses/by-sa/1.0/nl/
http://creativecommons.org/licenses/by-sa/1.0/
http://creativecommons.org/licenses/by-sa/2.0/at/
http://creativecommons.org/licenses/by-sa/2.0/au/
http://creativecommons.org/licenses/by-sa/2.0/be/
http://creativecommons.org/licenses/by-sa/2.0/br/
http://creativecommons.org/licenses/by-sa/2.0/ca/
http://creativecommons.org/licenses/by-sa/2.0/cl/
http://creativecommons.org/licenses/by-sa/2.0/de/
http://creativecommons.org/licenses/by-sa/2.0/es/
http://creativecommons.org/licenses/by-sa/2.0/fr/
http://creativecommons.org/licenses/by-sa/2.0/hr/
http://creativecommons.org/licenses/by-sa/2.0/it/
http://creativecommons.org/licenses/by-sa/2.0/jp/
http://creativecommons.org/licenses/by-sa/2.0/kr/
http://creativecommons.org/licenses/by-sa/2.0/nl/
http://creativecommons.org/licenses/by-sa/2.0/pl/
http://creativecommons.org/licenses/by-sa/2.0/
http://creativecommons.org/licenses/by-sa/2.0/tw/
http://creativecommons.org/licenses/by-sa/2.0/uk/
http://creativecommons.org/licenses/by-sa/2.0/za/
http://creativecommons.org/licenses/by-sa/2.1/au/
http://creativecommons.org/licenses/by-sa/2.1/es/
http://creativecommons.org/licenses/by-sa/2.1/jp/
http://creativecommons.org/licenses/by-sa/2.5/ar/
http://creativecommons.org/licenses/by-sa/2.5/au/
http://creativecommons.org/licenses/by-sa/2.5/bg/
http://creativecommons.org/licenses/by-sa/2.5/br/
http://creativecommons.org/licenses/by-sa/2.5/ca/
http://creativecommons.org/licenses/by-sa/2.5/ch/
http://creativecommons.org/licenses/by-sa/2.5/cn/
http://creativecommons.org/licenses/by-sa/2.5/co/
http://creativecommons.org/licenses/by-sa/2.5/dk/
http://creativecommons.org/licenses/by-sa/2.5/es/
http://creativecommons.org/licenses/by-sa/2.5/hr/
http://creativecommons.org/licenses/by-sa/2.5/hu/
http://creativecommons.org/licenses/by-sa/2.5/il/
http://creativecommons.org/licenses/by-sa/2.5/in/
http://creativecommons.org/licenses/by-sa/2.5/it/
http://creativecommons.org/licenses/by-sa/2.5/mk/
http://creativecommons.org/licenses/by-sa/2.5/mt/
http://creativecommons.org/licenses/by-sa/2.5/mx/
http://creativecommons.org/licenses/by-sa/2.5/my/
http://creativecommons.org/licenses/by-sa/2.5/nl/
http://creativecommons.org/licenses/by-sa/2.5/pe/
http://creativecommons.org/licenses/by-sa/2.5/pl/
http://creativecommons.org/licenses/by-sa/2.5/pt/
http://creativecommons.org/licenses/by-sa/2.5/
http://creativecommons.org/licenses/by-sa/2.5/scotland/
http://creativecommons.org/licenses/by-sa/2.5/se/
http://creativecommons.org/licenses/by-sa/2.5/si/
http://creativecommons.org/licenses/by-sa/2.5/tw/
http://creativecommons.org/licenses/by-sa/2.5/za/
http://creativecommons.org/licenses/by-sa/3.0/
http://creativecommons.org/licenses/by-sa/3.0/us/
http://creativecommons.org/licenses/devnations/2.0/
http://creativecommons.org/licenses/GPL/2.0/
http://creativecommons.org/licenses/LGPL/2.1/
http://creativecommons.org/licenses/nc/1.0/fi/
http://creativecommons.org/licenses/nc/1.0/nl/
http://creativecommons.org/licenses/nc/1.0/
http://creativecommons.org/licenses/nc/2.0/jp/
http://creativecommons.org/licenses/nc-sa/1.0/fi/
http://creativecommons.org/licenses/nc-sa/1.0/nl/
http://creativecommons.org/licenses/nc-sa/1.0/
http://creativecommons.org/licenses/nc-sa/2.0/jp/
http://creativecommons.org/licenses/nc-sampling+/1.0/
http://creativecommons.org/licenses/nc-sampling+/1.0/tw/
http://creativecommons.org/licenses/nd/1.0/fi/
http://creativecommons.org/licenses/nd/1.0/nl/
http://creativecommons.org/licenses/nd/1.0/
http://creativecommons.org/licenses/nd/2.0/jp/
http://creativecommons.org/licenses/nd-nc/1.0/fi/
http://creativecommons.org/licenses/nd-nc/1.0/nl/
http://creativecommons.org/licenses/nd-nc/1.0/
http://creativecommons.org/licenses/nd-nc/2.0/jp/
http://creativecommons.org/licenses/publicdomain/
http://creativecommons.org/licenses/sa/1.0/fi/
http://creativecommons.org/licenses/sa/1.0/nl/
http://creativecommons.org/licenses/sa/1.0/
http://creativecommons.org/licenses/sa/2.0/jp/
http://creativecommons.org/licenses/sampling/1.0/br/
http://creativecommons.org/licenses/sampling+/1.0/br/
http://creativecommons.org/licenses/sampling+/1.0/de/
http://creativecommons.org/licenses/sampling/1.0/
http://creativecommons.org/licenses/sampling+/1.0/
http://creativecommons.org/licenses/sampling/1.0/tw/
http://creativecommons.org/licenses/sampling+/1.0/tw/

BIN
tests/files/test.mp3 Normal file

Binary file not shown.

View file

@ -5,6 +5,7 @@ EXPORTS
gst_codec_utils_aac_get_sample_rate_from_index
gst_codec_utils_h264_caps_set_level_and_profile
gst_codec_utils_h264_get_level
gst_codec_utils_h264_get_level_idc
gst_codec_utils_h264_get_profile
gst_codec_utils_mpeg4video_caps_set_level_and_profile
gst_codec_utils_mpeg4video_get_level

View file

@ -5,19 +5,29 @@ EXPORTS
gst_tag_from_id3_tag
gst_tag_from_id3_user_tag
gst_tag_from_vorbis_tag
gst_tag_get_id3v2_tag_size
gst_tag_get_language_code_iso_639_1
gst_tag_get_language_code_iso_639_2B
gst_tag_get_language_code_iso_639_2T
gst_tag_get_language_codes
gst_tag_get_language_name
gst_tag_get_license_description
gst_tag_get_license_flags
gst_tag_get_license_jurisdiction
gst_tag_get_license_nick
gst_tag_get_license_title
gst_tag_get_license_version
gst_tag_get_licenses
gst_tag_id3_genre_count
gst_tag_id3_genre_get
gst_tag_image_data_to_image_buffer
gst_tag_image_type_get_type
gst_tag_license_flags_get_type
gst_tag_list_add_id3_image
gst_tag_list_from_exif_buffer
gst_tag_list_from_exif_buffer_with_tiff_header
gst_tag_list_from_vorbiscomment
gst_tag_list_from_id3v2_tag
gst_tag_list_from_vorbiscomment_buffer
gst_tag_list_from_xmp_buffer
gst_tag_list_new_from_id3v1
@ -26,6 +36,7 @@ EXPORTS
gst_tag_list_to_vorbiscomment_buffer
gst_tag_list_to_xmp_buffer
gst_tag_list_to_xmp_buffer_full
gst_tag_mux_get_type
gst_tag_parse_extended_comment
gst_tag_register_musicbrainz_tags
gst_tag_to_id3_tag