Commit 088c7c07 authored by Tim-Philipp Müller's avatar Tim-Philipp Müller

tag: add some utility functions for language codes and tags

Add some utility functions for language tags and ISO-639
codes. These are useful for both GUIs and elements. The
iso-codes package is used for language name translations
if available.

API: gst_tag_get_language_codes()
API: gst_tag_get_language_name()
API: gst_tag_get_language_code()
API: gst_tag_get_language_code_iso_639_1()
API: gst_tag_get_language_code_iso_639_2B()
API: gst_tag_get_language_code_iso_639_2T()
parent e897373a
......@@ -42,3 +42,5 @@ Makefile.in
Makefile
*.gir
*.typelib
gst-libs/gst/tag/mklangtables
......@@ -348,6 +348,56 @@ if test "x$HAVE_SYS_SOCKET_H" != "xyes"; then
AG_GST_DISABLE_PLUGIN(tcp)
fi
dnl iso-codes is optional, used by libgsttag
AC_ARG_ENABLE(iso-codes,
AC_HELP_STRING([--enable-iso-codes],[use iso-codes if installed]),
[case "${enableval}" in
yes) enable_iso_codes=yes ;;
no) enable_iso_codes=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-iso-codes) ;;
esac
],
[enable_iso_codes=yes]) dnl Default value
AC_MSG_CHECKING([whether to use iso-codes if they are available])
if test "x$enable_iso_codes" = "xyes"; then
AC_MSG_RESULT([yes])
have_iso_codes=no
AC_MSG_CHECKING([whether iso-codes are available on this system])
if $PKG_CONFIG iso-codes; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([whether iso-codes has iso-639 domain])
if $PKG_CONFIG --variable=domains iso-codes | $GREP -q 639 ; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([for iso-codes prefix])
ISO_CODES_PREFIX=`$PKG_CONFIG --variable=prefix iso-codes`
if test -d "$ISO_CODES_PREFIX"; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([ISO_CODES_PREFIX])
AC_MSG_RESULT([$ISO_CODES_PREFIX])
ISO_639_DOMAIN="iso_639"
AC_MSG_CHECKING([ISO_639_DOMAIN])
AC_MSG_RESULT([$ISO_639_DOMAIN])
have_iso_codes=yes
AC_DEFINE([HAVE_ISO_CODES], [1], [make use of iso-codes for ISO-639])
AC_DEFINE_UNQUOTED([ISO_CODES_PREFIX], ["$ISO_CODES_PREFIX"], [prefix])
ISO_CODES_VERSION=`$PKG_CONFIG --modversion iso-codes`
AC_DEFINE_UNQUOTED([ISO_CODES_VERSION], ["$ISO_CODES_VERSION"], [ ])
else
AC_MSG_RESULT([no])
fi
else
AC_MSG_RESULT([no])
fi
else
AC_MSG_RESULT([no])
fi
AM_CONDITIONAL(USE_ISO_CODES, test "x$have_iso_codes" = "xyes")
else
AC_MSG_RESULT([no (disabled via --disable-iso-codes)])
AM_CONDITIONAL(USE_ISO_CODES, false)
fi
dnl *** sys plug-ins ***
echo
......@@ -847,6 +897,7 @@ sed \
-e 's/.* HAVE_CPU_I386$/#define HAVE_CPU_I386 1/' \
-e 's/.* HAVE_FGETPOS$/#define HAVE_FGETPOS 1/' \
-e 's/.* HAVE_FSETPOS$/#define HAVE_FSETPOS 1/' \
-e 's/.* HAVE_ISO_CODES$/#undef HAVE_ISO_CODES/' \
-e 's/.* HAVE_LIBXML2$/#define HAVE_LIBXML2 1/' \
-e 's/.* HAVE_PROCESS_H$/#define HAVE_PROCESS_H 1/' \
-e 's/.* HAVE_STDLIB_H$/#define HAVE_STDLIB_H 1/' \
......@@ -856,6 +907,8 @@ sed \
-e 's/.* HAVE_WIN32$/#define HAVE_WIN32 1/' \
-e 's/.* HAVE_WINSOCK2_H$/#define HAVE_WINSOCK2_H 1/' \
-e 's/.* HOST_CPU$/#define HOST_CPU "i686"/' \
-e 's/.* ISO_CODES_PREFIX$/#undef ISO_CODES_PREFIX/' \
-e 's/.* ISO_CODES_VERSION$/#undef ISO_CODES_VERSION/' \
-e 's/.* LIBDIR$/#ifdef _DEBUG\n# define LIBDIR PREFIX "\\\\debug\\\\lib"\n#else\n# define LIBDIR PREFIX "\\\\lib"\n#endif/' \
-e 's/.* LOCALEDIR$/#define LOCALEDIR PREFIX "\\\\share\\\\locale"/' \
-e "s/.* PACKAGE$/#define PACKAGE \"$PACKAGE\"/" \
......
......@@ -192,6 +192,7 @@
<xi:include href="xml/gsttagvorbis.xml" />
<xi:include href="xml/gsttagid3.xml" />
<xi:include href="xml/gsttagdemux.xml" />
<xi:include href="xml/gsttaglanguagecodes.xml" />
</chapter>
<chapter id="gstreamer-base-utils">
......
......@@ -1564,6 +1564,18 @@ GstTagDemuxClass
GstTagDemuxResult
</SECTION>
<SECTION>
<FILE>gsttaglanguagecodes</FILE>
<INCLUDE>gst/tag/tag.h</INCLUDE>
<SUBSECTION>
gst_tag_get_language_codes
gst_tag_get_language_name
gst_tag_get_language_code
gst_tag_get_language_code_iso_639_1
gst_tag_get_language_code_iso_639_2B
gst_tag_get_language_code_iso_639_2T
</SECTION>
# base utils
<SECTION>
......
......@@ -6,7 +6,7 @@ libgsttaginclude_HEADERS = \
lib_LTLIBRARIES = libgsttag-@GST_MAJORMINOR@.la
libgsttag_@GST_MAJORMINOR@_la_SOURCES = gstvorbistag.c gstid3tag.c tags.c gsttagdemux.c
libgsttag_@GST_MAJORMINOR@_la_SOURCES = gstvorbistag.c gstid3tag.c lang.c tags.c gsttagdemux.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)
libgsttag_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
......@@ -49,3 +49,17 @@ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib)
CLEANFILES = $(BUILT_GIRSOURCES) $(typelibs_DATA)
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)
This diff is collapsed.
This diff is collapsed.
/* GStreamer Language Tag Utility Functions
* Copyright (C) 2009 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.
*/
/* mklangtables.c:
* little program that reads iso_639.xml and outputs tables for us as fallback
* for when iso-codes are not available or we fail to read the file for some
* reason, and so we don't have to parse the xml file just to map codes.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#include <string.h>
#define ISO_639_XML_PATH ISO_CODES_PREFIX "/share/xml/iso-codes/iso_639.xml"
typedef struct
{
gchar code_1[3]; /* de */
gchar code_2t[4]; /* deu */
gchar code_2b[4]; /* ger */
const gchar *name; /* German */
guint name_offset; /* offset into string table */
} IsoLang;
static GArray *languages = NULL;
static void
dump_languages (void)
{
GString *names;
const char *s;
int i, num_escaped;
g_assert (languages != NULL);
names = g_string_new ("");
g_print ("/* generated by " __FILE__ " iso-codes " ISO_CODES_VERSION " */\n");
g_print ("\n");
g_print ("#include <glib.h>\n");
g_print ("\n");
g_print ("#define ISO_639_FLAG_2T (1 << 0)\n");
g_print ("#define ISO_639_FLAG_2B (1 << 1)\n");
g_print ("\n");
g_print ("/* *INDENT-OFF* */\n");
g_print ("\n");
g_print ("static const struct\n");
g_print ("{\n");
g_print (" const gchar iso_639_1[3];\n");
g_print (" const gchar iso_639_2[4];\n");
g_print (" guint8 flags;\n");
g_print (" guint16 name_offset;\n");
g_print ("} iso_639_codes[] = {\n");
for (i = 0, num_escaped = 0; i < languages->len; ++i) {
IsoLang *lang = &g_array_index (languages, IsoLang, i);
/* For now just print those where there's both a ISO-639-1 and -2 code */
if (lang->code_1[0] == '\0')
continue;
/* save current offset */
lang->name_offset = names->len;
/* adjust for fact that \000 is 4 chars now but will take up only 1 later */
lang->name_offset -= num_escaped * 3;
/* append one char at a time, making sure to escape UTF-8 characters */
for (s = lang->name; s != NULL && *s != '\0'; ++s) {
if (g_ascii_isprint (*s) && *s != '"' && *s != '\\') {
g_string_append_c (names, *s);
} else {
g_string_append_printf (names, "\\%03o", (unsigned char) *s);
++num_escaped;
}
}
g_string_append (names, "\\000");
++num_escaped;
g_print (" /* %s */\n", lang->name);
if (strcmp (lang->code_2b, lang->code_2t) == 0) {
g_print (" { \"%s\", \"%s\", ISO_639_FLAG_2T | ISO_639_FLAG_2B, %u },\n",
lang->code_1, lang->code_2t, lang->name_offset);
} else {
/* if 639-2T and 639-2B differ, put 639-2T first */
g_print (" { \"%s\", \"%s\", ISO_639_FLAG_2T, %u },\n",
lang->code_1, lang->code_2t, lang->name_offset);
g_print (" { \"%s\", \"%s\", ISO_639_FLAG_2B, %u },\n",
lang->code_1, lang->code_2b, lang->name_offset);
}
}
g_print ("};\n");
g_print ("\n");
g_print ("const gchar iso_639_names[] =\n");
s = names->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");
g_print ("\n");
g_print ("/* *INDENT-ON* */\n");
g_string_free (names, TRUE);
}
static gboolean
copy_attribute (gchar * dest, guint dest_len, const gchar ** attr_names,
const gchar ** attr_vals, const gchar * needle)
{
while (attr_names != NULL && *attr_names != NULL) {
if (strcmp (*attr_names, needle) == 0) {
g_strlcpy (dest, *attr_vals, dest_len);
return TRUE;
}
++attr_names;
++attr_vals;
}
dest[0] = '\0';
return FALSE;
}
static void
xml_start_element (GMarkupParseContext * ctx, const gchar * element_name,
const gchar ** attr_names, const gchar ** attr_vals,
gpointer user_data, GError ** error)
{
gchar name[256];
IsoLang lang;
if (strcmp (element_name, "iso_639_entry") != 0)
return;
copy_attribute (lang.code_1, 3, attr_names, attr_vals, "iso_639_1_code");
copy_attribute (lang.code_2t, 4, attr_names, attr_vals, "iso_639_2T_code");
copy_attribute (lang.code_2b, 4, attr_names, attr_vals, "iso_639_2B_code");
copy_attribute (name, sizeof (name), attr_names, attr_vals, "name");
lang.name = g_intern_string (name);
g_array_append_val (languages, lang);
}
static void
parse_iso_639_xml (const gchar * data, gsize len)
{
GMarkupParser xml_parser = { xml_start_element, NULL, NULL, NULL, NULL };
GMarkupParseContext *ctx;
GError *err = NULL;
g_return_if_fail (g_utf8_validate (data, len, NULL));
ctx = g_markup_parse_context_new (&xml_parser, 0, NULL, NULL);
if (!g_markup_parse_context_parse (ctx, data, len, &err))
g_error ("Parsing failed: %s", err->message);
g_markup_parse_context_free (ctx);
}
static gint
languages_sort_func (IsoLang * l1, IsoLang * l2)
{
if (l1 == l2)
return 0;
if (l1->code_1[0] == '\0' && l2->code_1[0] != '\0')
return -1;
return strcmp (l1->code_1, l2->code_1);
}
int
main (int argc, char **argv)
{
GMappedFile *f;
gchar *xml_data;
gsize xml_len;
f = g_mapped_file_new (ISO_639_XML_PATH, FALSE, NULL);
if (f != NULL) {
xml_data = (gchar *) g_mapped_file_get_contents (f);
xml_len = g_mapped_file_get_length (f);
} else {
GError *err = NULL;
if (!g_file_get_contents (ISO_639_XML_PATH, &xml_data, &xml_len, &err))
g_error ("Could not read %s: %s", ISO_639_XML_PATH, err->message);
}
languages = g_array_new (FALSE, TRUE, sizeof (IsoLang));
parse_iso_639_xml (xml_data, xml_len);
g_array_sort (languages, (GCompareFunc) languages_sort_func);
dump_languages ();
g_array_free (languages, TRUE);
if (f != NULL)
g_mapped_file_unref (f);
else
g_free (xml_data);
return 0;
}
......@@ -238,6 +238,30 @@ GstBuffer * gst_tag_image_data_to_image_buffer (const guint8 * ima
/* FIXME 0.11: replace with a more general gst_tag_library_init() */
void gst_tag_register_musicbrainz_tags (void);
/* language tag related functions */
gchar ** gst_tag_get_language_codes (void);
const gchar * gst_tag_get_language_name (const gchar * language_code);
const gchar * gst_tag_get_language_code_iso_639_1 (const gchar * lang_code);
const gchar * gst_tag_get_language_code_iso_639_2B (const gchar * lang_code);
const gchar * gst_tag_get_language_code_iso_639_2T (const gchar * lang_code);
/**
* gst_tag_get_language_code:
* @lang_code: ISO-639 language code (e.g. "deu" or "ger" or "de")
*
* Convenience macro wrapping gst_tag_get_language_code_iso_639_1().
*
* Since: 0.10.26
*/
#define gst_tag_get_language_code(lang_code) \
gst_tag_get_language_code_iso_639_1(lang_code)
G_END_DECLS
#endif /* __GST_TAG_TAG_H__ */
......@@ -2,7 +2,7 @@
*
* unit tests for the tag support library
*
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* Copyright (C) 2006-2009 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
......@@ -682,6 +682,56 @@ GST_START_TEST (test_id3v1_utf8_tag)
GST_END_TEST;
GST_START_TEST (test_language_utils)
{
gchar **lang_codes, **c;
lang_codes = gst_tag_get_language_codes ();
fail_unless (lang_codes != NULL);
fail_unless (*lang_codes != NULL);
for (c = lang_codes; c != NULL && *c != NULL; ++c) {
const gchar *lang_name;
lang_name = gst_tag_get_language_name (*c);
GST_DEBUG ("[%s] %s\n", *c, GST_STR_NULL (lang_name));
fail_unless (lang_name != NULL);
fail_unless (g_utf8_validate (lang_name, -1, NULL));
}
g_strfreev (lang_codes);
fail_unless (gst_tag_get_language_name ("de") != NULL);
fail_unless (gst_tag_get_language_name ("deu") != NULL);
fail_unless (gst_tag_get_language_name ("ger") != NULL);
fail_unless_equals_string (gst_tag_get_language_name ("deu"),
gst_tag_get_language_name ("ger"));
fail_unless_equals_string (gst_tag_get_language_name ("de"),
gst_tag_get_language_name ("ger"));
fail_unless (gst_tag_get_language_name ("de") !=
gst_tag_get_language_name ("fr"));
#define ASSERT_STRINGS_EQUAL fail_unless_equals_string
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code ("deu"), "de");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code ("de"), "de");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code ("ger"), "de");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_1 ("deu"), "de");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_1 ("de"), "de");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_1 ("ger"), "de");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_2T ("de"), "deu");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_2T ("deu"), "deu");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_2T ("ger"), "deu");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_2B ("de"), "ger");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_2B ("deu"), "ger");
ASSERT_STRINGS_EQUAL (gst_tag_get_language_code_iso_639_2B ("ger"), "ger");
}
GST_END_TEST;
static Suite *
tag_suite (void)
{
......@@ -694,6 +744,7 @@ tag_suite (void)
tcase_add_test (tc_chain, test_vorbis_tags);
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);
return s;
}
......
......@@ -5,6 +5,11 @@ EXPORTS
gst_tag_from_id3_tag
gst_tag_from_id3_user_tag
gst_tag_from_vorbis_tag
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_id3_genre_count
gst_tag_id3_genre_get
gst_tag_image_data_to_image_buffer
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment