qtdemux: implement 3GPP (TS 26.244 V8.0.0) Asset metadata handling, Fixes #132193

Implements 3gpp iso metadata tags which are different from mov udta atoms.
This commit is contained in:
Marco Ballesio 2009-04-15 20:10:04 +03:00 committed by Stefan Kost
parent af7f3a50dd
commit 94d5d24cf0
5 changed files with 145 additions and 2 deletions

View file

@ -4335,6 +4335,111 @@ unknown_stream:
} }
} }
static inline gboolean
qtdemux_is_string_3gp (GstQTDemux * qtdemux, guint32 fourcc)
{
/* Detect if the tag must be handled as 3gpp - i18n metadata. The first
* check is for catching all the possible brands, e.g. 3gp4,3gp5,.. and
* handling properly the tags present in more than one brand.*/
return ((qtdemux->major_brand & GST_MAKE_FOURCC (255, 255, 255, 0)) ==
GST_MAKE_FOURCC ('3', 'g', 'p', 0)
&& (fourcc == FOURCC_cprt || fourcc == FOURCC_gnre
|| fourcc == FOURCC_kywd)) || fourcc == FOURCC_titl
|| fourcc == FOURCC_dscp || fourcc == FOURCC_perf || fourcc == FOURCC_auth
|| fourcc == FOURCC_albm;
}
static void
qtdemux_tag_add_location (GstQTDemux * qtdemux, const char *tag,
const char *dummy, GNode * node)
{
const gchar *env_vars[] = { "GST_QT_TAG_ENCODING", "GST_TAG_ENCODING", NULL };
int offset;
char *name;
gdouble longitude, latitude, altitude;
offset = 14;
/* TODO: language code skipped */
name = gst_tag_freeform_string_to_utf8 ((char *) node->data + offset,
-1, env_vars);
if (!name) {
GST_DEBUG_OBJECT (qtdemux, "failed to convert %s tag to UTF-8", tag);
}
/* +1 = skip location role byte */
offset += strlen (name) + 1 + 1;
longitude = QT_FP32 ((guint8 *) node->data + offset);
offset += 4;
latitude = QT_FP32 ((guint8 *) node->data + offset);
offset += 4;
altitude = QT_FP32 ((guint8 *) node->data + offset);
gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE,
GST_TAG_GEO_LOCATION_NAME, name,
GST_TAG_GEO_LOCATION_LATITUDE, latitude,
GST_TAG_GEO_LOCATION_LONGITUDE, longitude,
GST_TAG_GEO_LOCATION_ELEVATION, altitude, NULL);
/* TODO: no GST_TAG_, so astronomical body and additional notes skipped */
g_free (name);
}
static void
qtdemux_tag_add_year (GstQTDemux * qtdemux, const char *tag, const char *dummy,
GNode * node)
{
guint16 y;
GDate *date;
y = QT_UINT16 ((guint8 *) node->data + 12);
GST_DEBUG_OBJECT (qtdemux, "year: %u", y);
date = g_date_new_dmy (1, 1, y);
gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_REPLACE, tag, date, NULL);
g_date_free (date);
}
static void
qtdemux_tag_add_classification (GstQTDemux * qtdemux, const char *tag,
const char *dummy, GNode * node)
{
int offset;
char *tag_str = NULL;
guint32 entity;
guint16 table;
offset = 12;
entity = QT_FOURCC ((guint8 *) node->data + offset);
offset += 4;
table = QT_UINT16 ((guint8 *) node->data + offset);
/* Language code skipped */
offset += 4;
/* Tag format: "XXXX://Y[YYYY]/classification info string"
* XXXX: classification entity, fixed length 4 chars.
* Y[YYYY]: classification table, max 5 chars.
*/
tag_str = g_strdup_printf ("%" GST_FOURCC_FORMAT "://%u/%s",
GST_FOURCC_ARGS (entity), table, (char *) node->data + offset);
GST_DEBUG_OBJECT (qtdemux, "classification info: %s", tag_str);
gst_tag_list_add (qtdemux->tag_list, GST_TAG_MERGE_APPEND, tag,
tag_str, NULL);
g_free (tag_str);
}
static void static void
qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag, const char *dummy, qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag, const char *dummy,
GNode * node) GNode * node)
@ -4365,11 +4470,16 @@ qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag, const char *dummy,
} else { } else {
len = QT_UINT32 (node->data); len = QT_UINT32 (node->data);
type = QT_UINT32 ((guint8 *) node->data + 4); type = QT_UINT32 ((guint8 *) node->data + 4);
if (type & 0xa9000000) { if ((type >> 24) == 0xa9) {
/* Type starts with the (C) symbol, so the next 32 bits are /* Type starts with the (C) symbol, so the next 32 bits are
* the language code, which we ignore */ * the language code, which we ignore */
offset = 12; offset = 12;
GST_DEBUG_OBJECT (qtdemux, "found international text tag"); GST_DEBUG_OBJECT (qtdemux, "found international text tag");
} else if (qtdemux_is_string_3gp (qtdemux,
QT_FOURCC ((guint8 *) node->data + 4))) {
offset = 14;
/* 16-bit Language code is ignored here as well */
GST_DEBUG_OBJECT (qtdemux, "found 3gpp text tag");
} else { } else {
offset = 8; offset = 8;
GST_DEBUG_OBJECT (qtdemux, "found normal text tag"); GST_DEBUG_OBJECT (qtdemux, "found normal text tag");
@ -4563,15 +4673,21 @@ static const struct
} add_funcs[] = { } add_funcs[] = {
{ {
FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, { FOURCC__nam, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
FOURCC_titl, GST_TAG_TITLE, NULL, qtdemux_tag_add_str}, {
FOURCC__grp, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, { FOURCC__grp, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
FOURCC__wrt, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, { FOURCC__wrt, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, { FOURCC__ART, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
FOURCC_perf, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
FOURCC_auth, GST_TAG_ARTIST, NULL, qtdemux_tag_add_str}, {
FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, { FOURCC__alb, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
FOURCC_albm, GST_TAG_ALBUM, NULL, qtdemux_tag_add_str}, {
FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, { FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, { FOURCC__cpy, GST_TAG_COPYRIGHT, NULL, qtdemux_tag_add_str}, {
FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, { FOURCC__cmt, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, { FOURCC__des, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, qtdemux_tag_add_str}, {
FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, { FOURCC__day, GST_TAG_DATE, NULL, qtdemux_tag_add_date}, {
FOURCC_yrrc, GST_TAG_DATE, NULL, qtdemux_tag_add_year}, {
FOURCC__too, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, { FOURCC__too, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, { FOURCC__inf, GST_TAG_COMMENT, NULL, qtdemux_tag_add_str}, {
FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, { FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, qtdemux_tag_add_num}, {
@ -4585,7 +4701,10 @@ static const struct
FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, qtdemux_tag_add_covr}, { FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, qtdemux_tag_add_covr}, {
FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, { FOURCC_kywd, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, { FOURCC_keyw, GST_TAG_KEYWORDS, NULL, qtdemux_tag_add_str}, {
FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str} FOURCC__enc, GST_TAG_ENCODER, NULL, qtdemux_tag_add_str}, {
FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, qtdemux_tag_add_location}, {
FOURCC_clsf, GST_QT_DEMUX_CLASSIFICATION_TAG, NULL,
qtdemux_tag_add_classification}
}; };
static void static void

View file

@ -44,6 +44,7 @@ GST_DEBUG_CATEGORY_EXTERN (qtdemux_debug);
/* qtdemux produces these for atoms it cannot parse */ /* qtdemux produces these for atoms it cannot parse */
#define GST_QT_DEMUX_PRIVATE_TAG "private-qt-tag" #define GST_QT_DEMUX_PRIVATE_TAG "private-qt-tag"
#define GST_QT_DEMUX_CLASSIFICATION_TAG "classification"
#define GST_QTDEMUX_MAX_STREAMS 8 #define GST_QTDEMUX_MAX_STREAMS 8

View file

@ -140,6 +140,17 @@ G_BEGIN_DECLS
#define FOURCC_keyw GST_MAKE_FOURCC('k','e','y','w') #define FOURCC_keyw GST_MAKE_FOURCC('k','e','y','w')
#define FOURCC_kywd GST_MAKE_FOURCC('k','y','w','d') #define FOURCC_kywd GST_MAKE_FOURCC('k','y','w','d')
/* 3gpp asset meta data fourcc */
#define FOURCC_titl GST_MAKE_FOURCC('t','i','t','l')
#define FOURCC_dscp GST_MAKE_FOURCC('d','s','c','p')
#define FOURCC_perf GST_MAKE_FOURCC('p','e','r','f')
#define FOURCC_auth GST_MAKE_FOURCC('a','u','t','h')
#define FOURCC_rtng GST_MAKE_FOURCC('r','t','n','g')
#define FOURCC_clsf GST_MAKE_FOURCC('c','l','s','f')
#define FOURCC_loci GST_MAKE_FOURCC('l','o','c','i')
#define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m')
#define FOURCC_yrrc GST_MAKE_FOURCC('y','r','r','c')
/* ISO Motion JPEG 2000 fourcc */ /* ISO Motion JPEG 2000 fourcc */
#define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2') #define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2')
#define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h') #define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h')

View file

@ -95,14 +95,19 @@ static const QtNodeType qt_node_types[] = {
{FOURCC_meta, "meta", 0, qtdemux_dump_unknown}, {FOURCC_meta, "meta", 0, qtdemux_dump_unknown},
{FOURCC_ilst, "ilst", QT_FLAG_CONTAINER,}, {FOURCC_ilst, "ilst", QT_FLAG_CONTAINER,},
{FOURCC__nam, "Name", QT_FLAG_CONTAINER,}, {FOURCC__nam, "Name", QT_FLAG_CONTAINER,},
{FOURCC_titl, "Title", QT_FLAG_CONTAINER,},
{FOURCC__ART, "Artist", QT_FLAG_CONTAINER,}, {FOURCC__ART, "Artist", QT_FLAG_CONTAINER,},
{FOURCC_auth, "Author", QT_FLAG_CONTAINER,},
{FOURCC_perf, "Performer", QT_FLAG_CONTAINER,},
{FOURCC__wrt, "Writer", QT_FLAG_CONTAINER,}, {FOURCC__wrt, "Writer", QT_FLAG_CONTAINER,},
{FOURCC__grp, "Group", QT_FLAG_CONTAINER,}, {FOURCC__grp, "Group", QT_FLAG_CONTAINER,},
{FOURCC__alb, "Album", QT_FLAG_CONTAINER,}, {FOURCC__alb, "Album", QT_FLAG_CONTAINER,},
{FOURCC_albm, "Album", QT_FLAG_CONTAINER,},
{FOURCC__day, "Date", QT_FLAG_CONTAINER,}, {FOURCC__day, "Date", QT_FLAG_CONTAINER,},
{FOURCC__cpy, "Copyright", QT_FLAG_CONTAINER,}, {FOURCC__cpy, "Copyright", QT_FLAG_CONTAINER,},
{FOURCC__cmt, "Comment", QT_FLAG_CONTAINER,}, {FOURCC__cmt, "Comment", QT_FLAG_CONTAINER,},
{FOURCC__des, "Description", QT_FLAG_CONTAINER,}, {FOURCC__des, "Description", QT_FLAG_CONTAINER,},
{FOURCC_dscp, "Description", QT_FLAG_CONTAINER,},
{FOURCC__req, "Requirement", QT_FLAG_CONTAINER,}, {FOURCC__req, "Requirement", QT_FLAG_CONTAINER,},
{FOURCC__enc, "Encoder", QT_FLAG_CONTAINER,}, {FOURCC__enc, "Encoder", QT_FLAG_CONTAINER,},
{FOURCC_gnre, "Genre", QT_FLAG_CONTAINER,}, {FOURCC_gnre, "Genre", QT_FLAG_CONTAINER,},
@ -127,8 +132,11 @@ static const QtNodeType qt_node_types[] = {
{FOURCC_ctts, "Composition time to sample", 0, qtdemux_dump_ctts}, {FOURCC_ctts, "Composition time to sample", 0, qtdemux_dump_ctts},
{FOURCC_XiTh, "XiTh", 0}, {FOURCC_XiTh, "XiTh", 0},
{FOURCC_XdxT, "XdxT", 0}, {FOURCC_XdxT, "XdxT", 0},
{FOURCC_loci, "loci", 0},
{FOURCC_clsf, "clsf", 0},
{0, "unknown", 0,}, {0, "unknown", 0,},
}; };
static const int n_qt_node_types = static const int n_qt_node_types =
sizeof (qt_node_types) / sizeof (qt_node_types[0]); sizeof (qt_node_types) / sizeof (qt_node_types[0]);

View file

@ -41,6 +41,10 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_BUFFER, "QT atom", "unparsed QT tag atom", GST_TYPE_BUFFER, "QT atom", "unparsed QT tag atom",
gst_tag_merge_use_first); gst_tag_merge_use_first);
gst_tag_register (GST_QT_DEMUX_CLASSIFICATION_TAG, GST_TAG_FLAG_META,
G_TYPE_STRING, GST_QT_DEMUX_CLASSIFICATION_TAG, "content classification",
gst_tag_merge_use_first);
if (!gst_element_register (plugin, "qtdemux", if (!gst_element_register (plugin, "qtdemux",
GST_RANK_PRIMARY, GST_TYPE_QTDEMUX)) GST_RANK_PRIMARY, GST_TYPE_QTDEMUX))
return FALSE; return FALSE;