mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 19:20:35 +00:00
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:
commit
33467d9629
53 changed files with 12643 additions and 278 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -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
|
||||
|
|
14
configure.ac
14
configure.ac
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -66,6 +66,15 @@
|
|||
* Uh? What are you talking about?
|
||||
* I don'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>
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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@ \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
559
gst-libs/gst/tag/gsttagmux.c
Normal file
559
gst-libs/gst/tag/gsttagmux.c
Normal 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;
|
||||
}
|
89
gst-libs/gst/tag/gsttagmux.h
Normal file
89
gst-libs/gst/tag/gsttagmux.h
Normal 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
|
|
@ -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}
|
||||
};
|
||||
|
||||
|
|
1942
gst-libs/gst/tag/id3v2.3.0.txt
Normal file
1942
gst-libs/gst/tag/id3v2.3.0.txt
Normal file
File diff suppressed because it is too large
Load diff
1734
gst-libs/gst/tag/id3v2.4.0-frames.txt
Normal file
1734
gst-libs/gst/tag/id3v2.4.0-frames.txt
Normal file
File diff suppressed because it is too large
Load diff
733
gst-libs/gst/tag/id3v2.4.0-structure.txt
Normal file
733
gst-libs/gst/tag/id3v2.4.0-structure.txt
Normal 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
582
gst-libs/gst/tag/id3v2.c
Normal 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
120
gst-libs/gst/tag/id3v2.h
Normal 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
|
1168
gst-libs/gst/tag/id3v2frames.c
Normal file
1168
gst-libs/gst/tag/id3v2frames.c
Normal file
File diff suppressed because it is too large
Load diff
BIN
gst-libs/gst/tag/license-translations.dict
Normal file
BIN
gst-libs/gst/tag/license-translations.dict
Normal file
Binary file not shown.
467
gst-libs/gst/tag/licenses-tables.dat
Normal file
467
gst-libs/gst/tag/licenses-tables.dat
Normal 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
636
gst-libs/gst/tag/licenses.c
Normal 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;
|
||||
}
|
890
gst-libs/gst/tag/mklicensestables.c
Normal file
890
gst-libs/gst/tag/mklicensestables.c
Normal 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;
|
||||
}
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#define _USE_SSE2
|
||||
#define FLOATING_POINT
|
||||
#define DOUBLE_PRECISION
|
||||
#define OUTSIDE_SPEEX
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
2074
gst/playback/gstdecodebin.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 - %"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
376
tests/files/license-uris
Normal 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
BIN
tests/files/test.mp3
Normal file
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue