Map ffmpeg metadata to GStreamer tags

Update to the metadata API ffmpeg has had in
place for a long time now, and reenable output
of GStreamer tags from the demuxer.

https://bugzilla.gnome.org/show_bug.cgi?id=566605
This commit is contained in:
Jan Schmidt 2015-07-30 23:07:39 +10:00 committed by Nicolas Dufresne
parent c1333df059
commit 51896af52b

View file

@ -127,6 +127,7 @@ static gboolean gst_ffmpegdemux_sink_activate (GstPad * sinkpad,
GstObject * parent); GstObject * parent);
static gboolean gst_ffmpegdemux_sink_activate_mode (GstPad * sinkpad, static gboolean gst_ffmpegdemux_sink_activate_mode (GstPad * sinkpad,
GstObject * parent, GstPadMode mode, gboolean active); GstObject * parent, GstPadMode mode, gboolean active);
static GstTagList *gst_ffmpeg_metadata_to_tag_list (AVDictionary * metadata);
#if 0 #if 0
static gboolean static gboolean
@ -1028,7 +1029,10 @@ gst_ffmpegdemux_get_stream (GstFFMpegDemux * demux, AVStream * avstream)
/* metadata */ /* metadata */
if ((codec = gst_ffmpeg_get_codecid_longname (ctx->codec_id))) { if ((codec = gst_ffmpeg_get_codecid_longname (ctx->codec_id))) {
stream->tags = gst_tag_list_new_empty (); stream->tags = gst_ffmpeg_metadata_to_tag_list (avstream->metadata);
if (stream->tags == NULL)
stream->tags = gst_tag_list_new_empty ();
gst_tag_list_add (stream->tags, GST_TAG_MERGE_REPLACE, gst_tag_list_add (stream->tags, GST_TAG_MERGE_REPLACE,
(ctx->codec_type == AVMEDIA_TYPE_VIDEO) ? (ctx->codec_type == AVMEDIA_TYPE_VIDEO) ?
@ -1055,12 +1059,8 @@ unknown_caps:
} }
} }
#if 0
/* Re-enable once converted to new AVMetaData API
* See #566605
*/
static gchar * static gchar *
my_safe_copy (gchar * input) safe_utf8_copy (gchar * input)
{ {
gchar *output; gchar *output;
@ -1074,62 +1074,128 @@ my_safe_copy (gchar * input)
return output; return output;
} }
/* g_hash_table_insert requires non-const arguments, so
* we need to cast const strings to void * */
#define ADD_TAG_MAPPING(h, k, g) \
g_hash_table_insert ((h), (void *) (k), (void *) (g));
static GstTagList * static GstTagList *
gst_ffmpegdemux_read_tags (GstFFMpegDemux * demux) gst_ffmpeg_metadata_to_tag_list (AVDictionary * metadata)
{ {
GstTagList *tlist; GHashTable *tagmap = NULL;
gboolean hastag = FALSE; AVDictionaryEntry *tag = NULL;
GstTagList *list;
tlist = gst_tag_list_new (); if (g_once_init_enter (&tagmap)) {
GHashTable *tmp = g_hash_table_new (g_str_hash, g_str_equal);
if (*demux->context->title) { /* This is a list of standard tag keys taken from the avformat.h
gst_tag_list_add (tlist, GST_TAG_MERGE_REPLACE, * header, without handling any variants. */
GST_TAG_TITLE, my_safe_copy (demux->context->title), NULL); ADD_TAG_MAPPING (tmp, "album", GST_TAG_ALBUM);
hastag = TRUE; ADD_TAG_MAPPING (tmp, "album_artist", GST_TAG_ALBUM_ARTIST);
} ADD_TAG_MAPPING (tmp, "artist", GST_TAG_ALBUM_ARTIST);
if (*demux->context->author) { ADD_TAG_MAPPING (tmp, "comment", GST_TAG_COMMENT);
gst_tag_list_add (tlist, GST_TAG_MERGE_REPLACE, ADD_TAG_MAPPING (tmp, "composer", GST_TAG_COMPOSER);
GST_TAG_ARTIST, my_safe_copy (demux->context->author), NULL); ADD_TAG_MAPPING (tmp, "copyright", GST_TAG_COPYRIGHT);
hastag = TRUE; /* Need to convert ISO 8601 to GstDateTime: */
} ADD_TAG_MAPPING (tmp, "creation_time", GST_TAG_DATE_TIME);
if (*demux->context->copyright) { /* Need to convert ISO 8601 to GDateTime: */
gst_tag_list_add (tlist, GST_TAG_MERGE_REPLACE, ADD_TAG_MAPPING (tmp, "date", GST_TAG_DATE_TIME);
GST_TAG_COPYRIGHT, my_safe_copy (demux->context->copyright), NULL); ADD_TAG_MAPPING (tmp, "disc", GST_TAG_ALBUM_VOLUME_NUMBER);
hastag = TRUE; ADD_TAG_MAPPING (tmp, "encoder", GST_TAG_ENCODER);
} ADD_TAG_MAPPING (tmp, "encoded_by", GST_TAG_ENCODED_BY);
if (*demux->context->comment) { /* ADD_TAG_MAPPING (tmp, "filename", ); -- No mapping */
gst_tag_list_add (tlist, GST_TAG_MERGE_REPLACE, ADD_TAG_MAPPING (tmp, "genre", GST_TAG_GENRE);
GST_TAG_COMMENT, my_safe_copy (demux->context->comment), NULL); ADD_TAG_MAPPING (tmp, "language", GST_TAG_LANGUAGE_CODE);
hastag = TRUE; ADD_TAG_MAPPING (tmp, "performer", GST_TAG_PERFORMER);
} ADD_TAG_MAPPING (tmp, "publisher", GST_TAG_PUBLISHER);
if (*demux->context->album) { /* ADD_TAG_MAPPING(tmp, "service_name", ); -- No mapping */
gst_tag_list_add (tlist, GST_TAG_MERGE_REPLACE, /* ADD_TAG_MAPPING(tmp, "service_provider", ); -- No mapping */
GST_TAG_ALBUM, my_safe_copy (demux->context->album), NULL); ADD_TAG_MAPPING (tmp, "title", GST_TAG_TITLE);
hastag = TRUE; ADD_TAG_MAPPING (tmp, "track", GST_TAG_TRACK_NUMBER);
}
if (demux->context->track) { g_once_init_leave (&tagmap, tmp);
gst_tag_list_add (tlist, GST_TAG_MERGE_REPLACE,
GST_TAG_TRACK_NUMBER, demux->context->track, NULL);
hastag = TRUE;
}
if (*demux->context->genre) {
gst_tag_list_add (tlist, GST_TAG_MERGE_REPLACE,
GST_TAG_GENRE, my_safe_copy (demux->context->genre), NULL);
hastag = TRUE;
}
if (demux->context->year) {
gst_tag_list_add (tlist, GST_TAG_MERGE_REPLACE,
GST_TAG_DATE, g_date_new_dmy (1, 1, demux->context->year), NULL);
hastag = TRUE;
} }
if (!hastag) { list = gst_tag_list_new_empty ();
gst_tag_list_unref (tlist);
tlist = NULL; while ((tag = av_dict_get (metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
const gchar *gsttag = g_hash_table_lookup (tagmap, tag->key);
GType t;
GST_LOG ("mapping tag %s=%s\n", tag->key, tag->value);
if (gsttag == NULL) {
GST_LOG ("Ignoring unknown metadata tag %s", tag->key);
continue;
}
/* Special case, track and disc numbers may be x/n in libav, split
* them */
if (g_str_equal (gsttag, GST_TAG_TRACK_NUMBER)) {
guint track, trackcount;
if (sscanf (tag->value, "%u/%u", &track, &trackcount) == 2) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
gsttag, track, GST_TAG_TRACK_COUNT, trackcount, NULL);
continue;
}
/* Fall through and handle as a single uint below */
} else if (g_str_equal (gsttag, GST_TAG_ALBUM_VOLUME_NUMBER)) {
guint disc, disc_count;
if (sscanf (tag->value, "%u/%u", &disc, &disc_count) == 2) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
gsttag, disc, GST_TAG_ALBUM_VOLUME_COUNT, disc_count, NULL);
continue;
}
/* Fall through and handle as a single uint below */
}
t = gst_tag_get_type (gsttag);
if (t == G_TYPE_STRING) {
gchar *s = safe_utf8_copy (tag->value);
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, gsttag, s, NULL);
g_free (s);
} else if (t == G_TYPE_UINT || t == G_TYPE_INT) {
gchar *end;
gint v = strtol (tag->value, &end, 10);
if (end == tag->value)
continue; /* Failed to parse */
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, gsttag, v, NULL);
} else if (t == G_TYPE_DATE) {
guint year, month, day;
GDate *date = NULL;
if (sscanf (tag->value, "%04u-%02u-%02u", &year, &month, &day) == 3) {
date = g_date_new_dmy (day, month, year);
} else {
/* Try interpreting just as a year */
gchar *end;
year = strtol (tag->value, &end, 10);
if (end != tag->value)
date = g_date_new_dmy (1, 1, year);
}
if (date) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, gsttag, date, NULL);
g_date_free (date);
}
} else if (t == GST_TYPE_DATE_TIME) {
gchar *s = safe_utf8_copy (tag->value);
GstDateTime *d = gst_date_time_new_from_iso8601_string (s);
g_free (s);
if (d) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, gsttag, d, NULL);
gst_date_time_unref (d);
}
} else {
GST_FIXME ("Unhandled tag %s", gsttag);
}
} }
return tlist;
if (gst_tag_list_is_empty (list)) {
gst_tag_list_unref (list);
return NULL;
}
return list;
} }
#endif
static gboolean static gboolean
gst_ffmpegdemux_open (GstFFMpegDemux * demux) gst_ffmpegdemux_open (GstFFMpegDemux * demux)
@ -1138,12 +1204,7 @@ gst_ffmpegdemux_open (GstFFMpegDemux * demux)
GstFFMpegDemuxClass *oclass = GstFFMpegDemuxClass *oclass =
(GstFFMpegDemuxClass *) G_OBJECT_GET_CLASS (demux); (GstFFMpegDemuxClass *) G_OBJECT_GET_CLASS (demux);
gint res, n_streams, i; gint res, n_streams, i;
#if 0
/* Re-enable once converted to new AVMetaData API
* See #566605
*/
GstTagList *tags; GstTagList *tags;
#endif
GstEvent *event; GstEvent *event;
GList *cached_events; GList *cached_events;
@ -1224,28 +1285,31 @@ gst_ffmpegdemux_open (GstFFMpegDemux * demux)
cached_events = g_list_delete_link (cached_events, cached_events); cached_events = g_list_delete_link (cached_events, cached_events);
} }
#if 0
/* Re-enable once converted to new AVMetaData API
* See #566605
*/
/* grab the global tags */ /* grab the global tags */
tags = gst_ffmpegdemux_read_tags (demux); tags = gst_ffmpeg_metadata_to_tag_list (demux->context->metadata);
if (tags) { if (tags) {
GST_INFO_OBJECT (demux, "global tags: %" GST_PTR_FORMAT, tags); GST_INFO_OBJECT (demux, "global tags: %" GST_PTR_FORMAT, tags);
gst_element_found_tags (GST_ELEMENT (demux), tags);
} }
#endif
/* now handle the stream tags */ /* now handle the stream tags */
for (i = 0; i < n_streams; i++) { for (i = 0; i < n_streams; i++) {
GstFFStream *stream; GstFFStream *stream;
stream = gst_ffmpegdemux_get_stream (demux, demux->context->streams[i]); stream = gst_ffmpegdemux_get_stream (demux, demux->context->streams[i]);
if (stream->tags != NULL && stream->pad != NULL) { if (stream->pad != NULL) {
GST_INFO_OBJECT (stream->pad, "stream tags: %" GST_PTR_FORMAT,
stream->tags); /* Global tags */
gst_pad_push_event (stream->pad, if (tags)
gst_event_new_tag (gst_tag_list_ref (stream->tags))); gst_pad_push_event (stream->pad,
gst_event_new_tag (gst_tag_list_ref (tags)));
/* Per-stream tags */
if (stream->tags != NULL) {
GST_INFO_OBJECT (stream->pad, "stream tags: %" GST_PTR_FORMAT,
stream->tags);
gst_pad_push_event (stream->pad,
gst_event_new_tag (gst_tag_list_ref (stream->tags)));
}
} }
} }