|
|
|
@ -73,23 +73,20 @@ static void free_geobtag(ID3v2ExtraMetaGEOB *geob) |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 |
|
|
|
|
*/ |
|
|
|
|
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; |
|
|
|
@ -104,7 +101,7 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, |
|
|
|
|
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--; |
|
|
|
@ -133,7 +130,7 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, |
|
|
|
|
// 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);) |
|
|
|
|
} |
|
|
|
@ -142,7 +139,7 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case ID3v2_ENCODING_UTF8: |
|
|
|
|
while (left && (!seeknull || ch)) { |
|
|
|
|
while (left && ch) { |
|
|
|
|
ch = avio_r8(pb); |
|
|
|
|
avio_w8(dynbuf, ch); |
|
|
|
|
left--; |
|
|
|
@ -155,10 +152,7 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, |
|
|
|
|
if (ch) |
|
|
|
|
avio_w8(dynbuf, 0); |
|
|
|
|
|
|
|
|
|
len = avio_close_dyn_buf(dynbuf, (uint8_t **)dst); |
|
|
|
|
if (dstlen) |
|
|
|
|
*dstlen = len; |
|
|
|
|
|
|
|
|
|
avio_close_dyn_buf(dynbuf, (uint8_t **)dst); |
|
|
|
|
*maxread = left; |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
@ -170,38 +164,40 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -234,17 +230,17 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, char * |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|