#include "libavutil/dict.h"
#include "avio_internal.h"
+const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
+ { "TALB", "album"},
+ { "TCOM", "composer"},
+ { "TCON", "genre"},
+ { "TCOP", "copyright"},
+ { "TENC", "encoded_by"},
+ { "TIT2", "title"},
+ { "TLAN", "language"},
+ { "TPE1", "artist"},
+ { "TPE2", "album_artist"},
+ { "TPE3", "performer"},
+ { "TPOS", "disc"},
+ { "TPUB", "publisher"},
+ { "TRCK", "track"},
+ { "TSSE", "encoder"},
+ { 0 }
+};
+
+const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
+ { "TDRL", "date"},
+ { "TDRC", "date"},
+ { "TDEN", "creation_time"},
+ { "TSOA", "album-sort"},
+ { "TSOP", "artist-sort"},
+ { "TSOT", "title-sort"},
+ { 0 }
+};
+
+static const AVMetadataConv id3v2_2_metadata_conv[] = {
+ { "TAL", "album"},
+ { "TCO", "genre"},
+ { "TT2", "title"},
+ { "TEN", "encoded_by"},
+ { "TP1", "artist"},
+ { "TP2", "album_artist"},
+ { "TP3", "performer"},
+ { "TRK", "track"},
+ { 0 }
+};
+
+
+const char ff_id3v2_tags[][4] = {
+ "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
+ "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
+ "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
+ "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
+ { 0 },
+};
+
+const char ff_id3v2_4_tags[][4] = {
+ "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
+ "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
+ { 0 },
+};
+
+const char ff_id3v2_3_tags[][4] = {
+ "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
+ { 0 },
+};
+
int ff_id3v2_match(const uint8_t *buf, const char * magic)
{
return buf[0] == magic[0] &&
/**
* Free GEOB type extra metadata.
*/
-static void free_geobtag(ID3v2ExtraMetaGEOB *geob)
+static void free_geobtag(void *obj)
{
+ ID3v2ExtraMetaGEOB *geob = obj;
av_free(geob->mime_type);
av_free(geob->file_name);
av_free(geob->description);
/**
* Decode characters to UTF-8 according to encoding type. The decoded buffer is
- * always null terminated.
+ * always null terminated. Stop reading when either *maxread bytes are read from
+ * pb or U+0000 character is found.
*
* @param dst Pointer where the address of the buffer with the decoded bytes is
* stored. Buffer must be freed by caller.
- * @param dstlen Pointer to an int where the length of the decoded string
- * is stored (in bytes, incl. null termination)
* @param maxread Pointer to maximum number of characters to read from the
* AVIOContext. After execution the value is decremented by the number of bytes
* actually read.
- * @seeknull If true, decoding stops after the first U+0000 character found, if
- * there is any before maxread is reached
- * @returns 0 if no error occured, dst is uninitialized on error
+ * @returns 0 if no error occurred, dst is uninitialized on error
*/
static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding,
- uint8_t **dst, int *dstlen, int *maxread, const int seeknull)
+ uint8_t **dst, int *maxread)
{
- int len, ret;
+ int ret;
uint8_t tmp;
uint32_t ch = 1;
int left = *maxread;
switch (encoding) {
case ID3v2_ENCODING_ISO8859:
- while (left && (!seeknull || ch)) {
+ while (left && ch) {
ch = avio_r8(pb);
PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
left--;
case ID3v2_ENCODING_UTF16BOM:
if ((left -= 2) < 0) {
av_log(s, AV_LOG_ERROR, "Cannot read BOM value, input too short\n");
- avio_close_dyn_buf(dynbuf, (uint8_t **)dst);
+ avio_close_dyn_buf(dynbuf, dst);
av_freep(dst);
return AVERROR_INVALIDDATA;
}
break;
default:
av_log(s, AV_LOG_ERROR, "Incorrect BOM value\n");
- avio_close_dyn_buf(dynbuf, (uint8_t **)dst);
+ avio_close_dyn_buf(dynbuf, dst);
av_freep(dst);
*maxread = left;
return AVERROR_INVALIDDATA;
// fall-through
case ID3v2_ENCODING_UTF16BE:
- while ((left > 1) && (!seeknull || ch)) {
+ while ((left > 1) && ch) {
GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;)
PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
}
break;
case ID3v2_ENCODING_UTF8:
- while (left && (!seeknull || ch)) {
+ while (left && ch) {
ch = avio_r8(pb);
avio_w8(dynbuf, ch);
left--;
if (ch)
avio_w8(dynbuf, 0);
- len = avio_close_dyn_buf(dynbuf, (uint8_t **)dst);
- if (dstlen)
- *dstlen = len;
-
+ avio_close_dyn_buf(dynbuf, dst);
*maxread = left;
return 0;
static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const char *key)
{
uint8_t *dst;
- const char *val = NULL;
- int len, dstlen;
+ int encoding, dict_flags = AV_DICT_DONT_OVERWRITE;
unsigned genre;
if (taglen < 1)
return;
+ encoding = avio_r8(pb);
taglen--; /* account for encoding type byte */
- if (decode_str(s, pb, avio_r8(pb), &dst, &dstlen, &taglen, 0) < 0) {
+ if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
return;
}
if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
&& (sscanf(dst, "(%d)", &genre) == 1 || sscanf(dst, "%d", &genre) == 1)
- && genre <= ID3v1_GENRE_MAX)
- val = ff_id3v1_genre_str[genre];
- else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
- /* dst now contains two 0-terminated strings */
- dst[dstlen] = 0;
- len = strlen(dst);
+ && genre <= ID3v1_GENRE_MAX) {
+ av_freep(&dst);
+ dst = ff_id3v1_genre_str[genre];
+ } else if (!(strcmp(key, "TXXX") && strcmp(key, "TXX"))) {
+ /* dst now contains the key, need to get value */
key = dst;
- val = dst + FFMIN(len + 1, dstlen);
+ if (decode_str(s, pb, encoding, &dst, &taglen) < 0) {
+ av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
+ av_freep(&key);
+ return;
+ }
+ dict_flags |= AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_STRDUP_KEY;
}
else if (*dst)
- val = dst;
-
- if (val)
- av_dict_set(&s->metadata, key, val, AV_DICT_DONT_OVERWRITE);
+ dict_flags |= AV_DICT_DONT_STRDUP_VAL;
- av_free(dst);
+ if (dst)
+ av_dict_set(&s->metadata, key, dst, dict_flags);
}
/**
taglen--;
/* read MIME type (always ISO-8859) */
- if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type, NULL, &taglen, 1) < 0
+ if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type, &taglen) < 0
|| taglen <= 0)
goto fail;
/* read file name */
- if (decode_str(s, pb, encoding, &geob_data->file_name, NULL, &taglen, 1) < 0
+ if (decode_str(s, pb, encoding, &geob_data->file_name, &taglen) < 0
|| taglen <= 0)
goto fail;
/* read content description */
- if (decode_str(s, pb, encoding, &geob_data->description, NULL, &taglen, 1) < 0
+ if (decode_str(s, pb, encoding, &geob_data->description, &taglen) < 0
|| taglen < 0)
goto fail;
av_dict_set(m, "date", date, 0);
}
+typedef struct ID3v2EMFunc {
+ const char *tag3;
+ const char *tag4;
+ void (*read)(AVFormatContext*, AVIOContext*, int, char*, ID3v2ExtraMeta **);
+ void (*free)(void *obj);
+} ID3v2EMFunc;
+
+static const ID3v2EMFunc id3v2_extra_meta_funcs[] = {
+ { "GEO", "GEOB", read_geobtag, free_geobtag },
+ { NULL }
+};
+
/**
* Get the corresponding ID3v2EMFunc struct for a tag.
* @param isv34 Determines if v2.2 or v2.3/4 strings are used
static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
{
int i = 0;
- while (ff_id3v2_extra_meta_funcs[i].tag3) {
+ while (id3v2_extra_meta_funcs[i].tag3) {
if (!memcmp(tag,
- (isv34 ?
- ff_id3v2_extra_meta_funcs[i].tag4 :
- ff_id3v2_extra_meta_funcs[i].tag3),
+ (isv34 ? id3v2_extra_meta_funcs[i].tag4 :
+ id3v2_extra_meta_funcs[i].tag3),
(isv34 ? 4 : 3)))
- return &ff_id3v2_extra_meta_funcs[i];
+ return &id3v2_extra_meta_funcs[i];
i++;
}
return NULL;
AVIOContext *pbx;
unsigned char *buffer = NULL;
int buffer_size = 0;
- void (*extra_func)(AVFormatContext*, AVIOContext*, int, char*, ID3v2ExtraMeta**) = NULL;
+ const ID3v2EMFunc *extra_func;
switch (version) {
case 2:
av_log(s, AV_LOG_WARNING, "Skipping encrypted/compressed ID3v2 frame %s.\n", tag);
avio_skip(s->pb, tlen);
/* check for text tag or supported special meta tag */
- } else if (tag[0] == 'T' || (extra_meta && (extra_func = get_extra_meta_func(tag, isv34)->read))) {
+ } else if (tag[0] == 'T' || (extra_meta && (extra_func = get_extra_meta_func(tag, isv34)))) {
if (unsync || tunsync) {
int i, j;
av_fast_malloc(&buffer, &buffer_size, tlen);
read_ttag(s, pbx, tlen, tag);
else
/* parse special meta tag */
- extra_func(s, pbx, tlen, tag, extra_meta);
+ extra_func->read(s, pbx, tlen, tag, extra_meta);
}
else if (!tag[0]) {
if (tag[1])
}
} 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, id3v2_2_metadata_conv);
ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
merge_date(&s->metadata);
}
void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
{
ID3v2ExtraMeta *current = *extra_meta, *next;
- void (*free_func)(ID3v2ExtraMeta*);
+ const ID3v2EMFunc *extra_func;
while (current) {
- if ((free_func = get_extra_meta_func(current->tag, 1)->free))
- free_func(current->data);
+ if ((extra_func = get_extra_meta_func(current->tag, 1)))
+ extra_func->free(current->data);
next = current->next;
av_freep(¤t);
current = next;
}
}
-
-const ID3v2EMFunc ff_id3v2_extra_meta_funcs[] = {
- { "GEO", "GEOB", read_geobtag, free_geobtag },
- { NULL }
-};
-
-const AVMetadataConv ff_id3v2_34_metadata_conv[] = {
- { "TALB", "album"},
- { "TCOM", "composer"},
- { "TCON", "genre"},
- { "TCOP", "copyright"},
- { "TENC", "encoded_by"},
- { "TIT2", "title"},
- { "TLAN", "language"},
- { "TPE1", "artist"},
- { "TPE2", "album_artist"},
- { "TPE3", "performer"},
- { "TPOS", "disc"},
- { "TPUB", "publisher"},
- { "TRCK", "track"},
- { "TSSE", "encoder"},
- { 0 }
-};
-
-const AVMetadataConv ff_id3v2_4_metadata_conv[] = {
- { "TDRL", "date"},
- { "TDRC", "date"},
- { "TDEN", "creation_time"},
- { "TSOA", "album-sort"},
- { "TSOP", "artist-sort"},
- { "TSOT", "title-sort"},
- { 0 }
-};
-
-const AVMetadataConv ff_id3v2_2_metadata_conv[] = {
- { "TAL", "album"},
- { "TCO", "genre"},
- { "TT2", "title"},
- { "TEN", "encoded_by"},
- { "TP1", "artist"},
- { "TP2", "album_artist"},
- { "TP3", "performer"},
- { "TRK", "track"},
- { 0 }
-};
-
-
-const char ff_id3v2_tags[][4] = {
- "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT",
- "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED",
- "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3",
- "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE",
- { 0 },
-};
-
-const char ff_id3v2_4_tags[][4] = {
- "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO",
- "TPRO", "TSOA", "TSOP", "TSOT", "TSST",
- { 0 },
-};
-
-const char ff_id3v2_3_tags[][4] = {
- "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER",
- { 0 },
-};