* 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
*/
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "metadata.h"
+#include "avio_internal.h"
int ff_id3v2_match(const uint8_t *buf, const char * magic)
{
{
int v = 0;
while (len--)
- v = (v << 7) + (get_byte(s) & 0x7F);
+ v = (v << 7) + (avio_r8(s) & 0x7F);
return v;
}
const char *val = NULL;
int len, dstlen = sizeof(dst) - 1;
unsigned genre;
- unsigned int (*get)(AVIOContext*) = get_be16;
+ unsigned int (*get)(AVIOContext*) = avio_rb16;
dst[0] = 0;
if (taglen < 1)
taglen--; /* account for encoding type byte */
- switch (get_byte(pb)) { /* encoding type */
+ switch (avio_r8(pb)) { /* encoding type */
case ID3v2_ENCODING_ISO8859:
q = dst;
while (taglen-- && q - dst < dstlen - 7) {
uint8_t tmp;
- PUT_UTF8(get_byte(pb), tmp, *q++ = tmp;)
+ PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
}
*q = 0;
break;
case ID3v2_ENCODING_UTF16BOM:
taglen -= 2;
- switch (get_be16(pb)) {
+ switch (avio_rb16(pb)) {
case 0xfffe:
- get = get_le16;
+ get = avio_rl16;
case 0xfeff:
break;
default:
case ID3v2_ENCODING_UTF8:
len = FFMIN(taglen, dstlen);
- get_buffer(pb, dst, len);
+ avio_read(pb, dst, len);
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 */
- url_fskip(s->pb, get_size(s->pb, 4));
+ avio_skip(s->pb, get_size(s->pb, 4));
while (len >= taghdrlen) {
unsigned int tflags;
int tunsync = 0;
if (isv34) {
- get_buffer(s->pb, tag, 4);
+ avio_read(s->pb, tag, 4);
tag[4] = 0;
if(version==3){
- tlen = get_be32(s->pb);
+ tlen = avio_rb32(s->pb);
}else
tlen = get_size(s->pb, 4);
- tflags = get_be16(s->pb);
+ tflags = avio_rb16(s->pb);
tunsync = tflags & ID3v2_FLAG_UNSYNCH;
} else {
- get_buffer(s->pb, tag, 3);
+ avio_read(s->pb, tag, 3);
tag[3] = 0;
- tlen = get_be24(s->pb);
+ 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;
-
- next = url_ftell(s->pb) + tlen;
+ }
+ len -= taghdrlen + tlen;
+ next = avio_tell(s->pb) + tlen;
if (tflags & ID3v2_FLAG_DATALEN) {
- get_be32(s->pb);
+ avio_rb32(s->pb);
tlen -= 4;
}
if (tflags & (ID3v2_FLAG_ENCRYPTION | ID3v2_FLAG_COMPRESSION)) {
av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
- url_fskip(s->pb, tlen);
+ avio_skip(s->pb, tlen);
} else if (tag[0] == 'T') {
if (unsync || tunsync) {
int i, j;
av_fast_malloc(&buffer, &buffer_size, tlen);
for (i = 0, j = 0; i < tlen; i++, j++) {
- buffer[j] = get_byte(s->pb);
+ buffer[j] = avio_r8(s->pb);
if (j > 0 && !buffer[j] && buffer[j - 1] == 0xff) {
/* Unsynchronised byte, skip it */
j--;
}
}
- init_put_byte(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
+ ffio_init_context(&pb, buffer, j, 0, NULL, NULL, NULL, NULL);
read_ttag(s, &pb, j, tag);
} else {
read_ttag(s, s->pb, tlen, tag);
else if (!tag[0]) {
if (tag[1])
av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding");
- url_fskip(s->pb, tlen);
+ avio_skip(s->pb, tlen);
break;
}
/* Skip to end of tag */
- url_fseek(s->pb, next, SEEK_SET);
+ avio_seek(s->pb, next, SEEK_SET);
}
- if (len > 0) {
- /* Skip padding */
- url_fskip(s->pb, len);
- }
if (version == 4 && flags & 0x10) /* Footer preset, always 10 bytes, skip over it */
- url_fskip(s->pb, 10);
-
- av_free(buffer);
- return;
+ end += 10;
error:
- av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n", version, reason);
- url_fskip(s->pb, len);
+ 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)
do {
/* save the current offset in case there's nothing to read/skip */
- off = url_ftell(s->pb);
- ret = get_buffer(s->pb, buf, ID3v2_HEADER_SIZE);
+ off = avio_tell(s->pb);
+ ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
if (ret != ID3v2_HEADER_SIZE)
break;
found_header = ff_id3v2_match(buf, magic);
(buf[9] & 0x7f);
ff_id3v2_parse(s, len, buf[3], buf[5]);
} else {
- url_fseek(s->pb, off, SEEK_SET);
+ avio_seek(s->pb, off, SEEK_SET);
}
} while (found_header);
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[] = {