* ID3v2 header parser
* Copyright (c) 2003 Fabrice Bellard
*
- * This file is part of FFmpeg.
+ * This file is part of Libav.
*
- * FFmpeg is free software; you can redistribute it and/or
+ * Libav is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * FFmpeg is distributed in the hope that it will be useful,
+ * Libav 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with FFmpeg; if not, write to the Free Software
+ * License along with Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
dst[len] = 0;
break;
default:
- av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s\n.", key);
+ av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s.\n", key);
}
if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
av_metadata_set2(&s->metadata, key, val, AV_METADATA_DONT_OVERWRITE);
}
+static int is_number(const char *str)
+{
+ while (*str >= '0' && *str <= '9') str++;
+ return !*str;
+}
+
+static AVMetadataTag* get_date_tag(AVMetadata *m, const char *tag)
+{
+ AVMetadataTag *t;
+ if ((t = av_metadata_get(m, tag, NULL, AV_METADATA_MATCH_CASE)) &&
+ strlen(t->value) == 4 && is_number(t->value))
+ return t;
+ return NULL;
+}
+
+static void merge_date(AVMetadata **m)
+{
+ AVMetadataTag *t;
+ char date[17] = {0}; // YYYY-MM-DD hh:mm
+
+ if (!(t = get_date_tag(*m, "TYER")) &&
+ !(t = get_date_tag(*m, "TYE")))
+ return;
+ av_strlcpy(date, t->value, 5);
+ av_metadata_set2(m, "TYER", NULL, 0);
+ av_metadata_set2(m, "TYE", NULL, 0);
+
+ if (!(t = get_date_tag(*m, "TDAT")) &&
+ !(t = get_date_tag(*m, "TDA")))
+ goto finish;
+ snprintf(date + 4, sizeof(date) - 4, "-%.2s-%.2s", t->value + 2, t->value);
+ av_metadata_set2(m, "TDAT", NULL, 0);
+ av_metadata_set2(m, "TDA", NULL, 0);
+
+ if (!(t = get_date_tag(*m, "TIME")) &&
+ !(t = get_date_tag(*m, "TIM")))
+ goto finish;
+ snprintf(date + 10, sizeof(date) - 10, " %.2s:%.2s", t->value, t->value + 2);
+ av_metadata_set2(m, "TIME", NULL, 0);
+ av_metadata_set2(m, "TIM", NULL, 0);
+
+finish:
+ if (date[0])
+ av_metadata_set2(m, "date", date, 0);
+}
+
static void ff_id3v2_parse(AVFormatContext *s, int len, uint8_t version, uint8_t flags)
{
int isv34, tlen, unsync;
char tag[5];
- int64_t next;
+ int64_t next, end = avio_tell(s->pb) + len;
int taghdrlen;
- const char *reason;
+ const char *reason = NULL;
AVIOContext pb;
unsigned char *buffer = NULL;
int buffer_size = 0;
unsync = flags & 0x80;
if (isv34 && flags & 0x40) /* Extended header present, just skip over it */
- avio_seek(s->pb, get_size(s->pb, 4), SEEK_CUR);
+ avio_skip(s->pb, get_size(s->pb, 4));
while (len >= taghdrlen) {
unsigned int tflags;
tag[3] = 0;
tlen = avio_rb24(s->pb);
}
- len -= taghdrlen + tlen;
-
- if (len < 0)
+ if (tlen < 0 || tlen > len - taghdrlen) {
+ av_log(s, AV_LOG_WARNING, "Invalid size in frame %s, skipping the rest of tag.\n", tag);
break;
-
+ }
+ len -= taghdrlen + tlen;
next = avio_tell(s->pb) + tlen;
if (tflags & ID3v2_FLAG_DATALEN) {
if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
- avio_seek(s->pb, tlen, SEEK_CUR);
+ avio_skip(s->pb, tlen);
} else if (tag[0] == 'T') {
if (unsync || tunsync) {
int i, j;
else if (!tag[0]) {
if (tag[1])
av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
- avio_seek(s->pb, tlen, SEEK_CUR);
+ avio_skip(s->pb, tlen);
break;
}
/* Skip to end of tag */
avio_seek(s->pb, next, SEEK_SET);
}
- if (len > 0) {
- /* Skip padding */
- avio_seek(s->pb, len, SEEK_CUR);
- }
if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
- avio_seek(s->pb, 10, SEEK_CUR);
-
- av_free(buffer);
- return;
+ end += 10;
error:
- av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
- avio_seek(s->pb, len, SEEK_CUR);
+ if (reason)
+ av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
+ avio_seek(s->pb, end, SEEK_SET);
av_free(buffer);
+ return;
}
void ff_id3v2_read(AVFormatContext *s, const char *magic)
ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv);
ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
+ merge_date(&s->metadata);
}
const AVMetadataConv ff_id3v2_34_metadata_conv[] = {