On Sun, 18 Sep 2011 18:24:53 +0200, David Goldwich <[email protected]>
wrote:
> From 0c8e1154f0dff93bb0ecb0f8f53973249ad1cbed Mon Sep 17 00:00:00 2001
> From: David Goldwich <[email protected]>
> Date: Sun, 18 Sep 2011 18:04:34 +0200
> Subject: [PATCH] id3v2: add support for non-text and GEOB type tag frames
>
> This extends the ID3v2 parser to allow for reading of non-text (i.e.
> other than T***) meta tag frames providing a ff_id3v2_read_all()
> function. An additional data structure 'ID3v2ExtraMeta' is introduced
> for these tags since AVDictionary is string oriented and unsuitable
> for binary data.
>
> A parser for tag frames of type GEOB is implemented, which is needed
> to extract keyring information from encrypted OMA files. GEOB data
> is parsed into 'ID3v2ExtraMetaGEOB' data structures.
>
> The routine to decode characters from different encodings to UTF-8,
> formerly part of the read_ttag() function, is moved to its own
> function. Because some tag frames contain subparts of unknown length,
> the function is now also able to read until a null character is found.
> In addition, the function now takes care of allocating a buffer long
> enough to hold the decoded characters.
>
> Signed-off-by: David Goldwich <[email protected]>
> ---
> libavformat/id3v2.c | 263 +++++++++++++++++++++++++++++++++++++++++++-------
> libavformat/id3v2.h | 38 +++++++-
> 2 files changed, 263 insertions(+), 38 deletions(-)
>
> diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
> index be6c03b..2a36934 100644
> --- a/libavformat/id3v2.c
> +++ b/libavformat/id3v2.c
> @@ -19,6 +19,7 @@
> * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA
> */
>
> +#include <inttypes.h>
> #include "id3v2.h"
> #include "id3v1.h"
> #include "libavutil/avstring.h"
> @@ -59,63 +60,126 @@ static unsigned int get_size(AVIOContext *s, int len)
> return v;
> }
>
> -static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const
> char *key)
> +/**
> + * Free GEOB type extra metadata.
> + */
> +static void free_geobtag(ID3v2ExtraMetaGEOB *geob)
> {
> - char *q, dst[512];
> - const char *val = NULL;
> - int len, dstlen = sizeof(dst) - 1;
> - unsigned genre;
> - unsigned int (*get)(AVIOContext*) = avio_rb16;
> + av_free(geob->mime_type);
> + av_free(geob->file_name);
> + av_free(geob->description);
> + av_free(geob->data);
> + av_free(geob);
> +}
>
> - dst[0] = 0;
> - if (taglen < 1)
> - return;
> +/**
> + * Decode characters to UTF-8 according to encoding type. The decoded buffer
> is
> + * always null terminated.
> + *
> + * @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
> + * @param key The tag frame key (for logging)
This parameter does not exist.
> + * @returns 0 if no error occured, otherwise a negative value is returned and
> + * dst is uninitialized
> + */
> +static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding,
> + uint8_t **dst, int *dstlen, int *maxread, const int
> seeknull)
> +{
> + int len;
> + uint8_t tmp;
> + uint32_t ch = 1;
> + unsigned int left = *maxread;
> + unsigned int (*get)(AVIOContext*) = avio_rb16;
> + AVIOContext *dynbuf;
>
> - taglen--; /* account for encoding type byte */
> + if (avio_open_dyn_buf(&dynbuf)) {
> + av_log(s, AV_LOG_ERROR, "Error opening memory stream\n");
> + return -1;
You should return the code from avio_open_dyn_buf() here.
> + }
>
> - switch (avio_r8(pb)) { /* encoding type */
> + switch (encoding) {
>
> case ID3v2_ENCODING_ISO8859:
> - q = dst;
> - while (taglen-- && q - dst < dstlen - 7) {
> - uint8_t tmp;
> - PUT_UTF8(avio_r8(pb), tmp, *q++ = tmp;)
> + while (left && (!seeknull || ch)) {
> + ch = avio_r8(pb);
> + PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
> + left--;
> }
> - *q = 0;
> break;
>
> case ID3v2_ENCODING_UTF16BOM:
> - taglen -= 2;
> + left -= 2;
left might be <= 2
> switch (avio_rb16(pb)) {
> case 0xfffe:
> get = avio_rl16;
> case 0xfeff:
> break;
> default:
> - av_log(s, AV_LOG_ERROR, "Incorrect BOM value in tag %s.\n", key);
> - return;
> + av_log(s, AV_LOG_ERROR, "Incorrect BOM value\n");
> + avio_close_dyn_buf(dynbuf, (uint8_t **)dst);
> + av_freep(dst);
> + *maxread = left;
> + return -1;
return AVERROR(EINVAL) please
> }
> // fall-through
>
> case ID3v2_ENCODING_UTF16BE:
> - q = dst;
> - while (taglen > 1 && q - dst < dstlen - 7) {
> - uint32_t ch;
> - uint8_t tmp;
> -
> - GET_UTF16(ch, ((taglen -= 2) >= 0 ? get(pb) : 0), break;)
> - PUT_UTF8(ch, tmp, *q++ = tmp;)
> + while ((left > 1) && (!seeknull || ch)) {
> + GET_UTF16(ch, ((left -= 2) >= 0 ? get(pb) : 0), break;)
> + PUT_UTF8(ch, tmp, avio_w8(dynbuf, tmp);)
> }
> - *q = 0;
> + if (left < 0)
> + left += 2; /* did not read last char from pb */
> break;
>
> case ID3v2_ENCODING_UTF8:
> - len = FFMIN(taglen, dstlen);
> - avio_read(pb, dst, len);
> - dst[len] = 0;
> + while (left && (!seeknull || ch)) {
> + ch = avio_r8(pb);
> + avio_w8(dynbuf, ch);
> + left--;
> + }
> break;
> default:
> - av_log(s, AV_LOG_WARNING, "Unknown encoding in tag %s.\n", key);
> + av_log(s, AV_LOG_WARNING, "Unknown encoding\n");
> + }
> +
> + if (ch)
> + avio_w8(dynbuf, 0);
> +
> + len = avio_close_dyn_buf(dynbuf, (uint8_t **)dst);
> + if (dstlen)
> + *dstlen = len;
> +
> + *maxread = left;
> +
> + return 0;
> +}
> +
> +/**
> + * Parse a text tag.
> + */
> +static void read_ttag(AVFormatContext *s, AVIOContext *pb, int taglen, const
> char *key)
> +{
> + uint8_t *dst;
> + const char *val = NULL;
> + int len, dstlen;
> + unsigned genre;
> +
> + if (taglen < 1)
> + return;
> +
> + taglen--; /* account for encoding type byte */
> +
> + if (decode_str(s, pb, avio_r8(pb), &dst, &dstlen, &taglen, 0) < 0) {
> + av_log(s, AV_LOG_ERROR, "Error reading frame %s, skipped\n", key);
> + return;
> }
>
> if (!(strcmp(key, "TCON") && strcmp(key, "TCO"))
> @@ -134,6 +198,77 @@ static void read_ttag(AVFormatContext *s, AVIOContext
> *pb, int taglen, const cha
>
> if (val)
> av_dict_set(&s->metadata, key, val, AV_DICT_DONT_OVERWRITE);
> +
> + av_free(dst);
> +}
> +
> +/**
> + * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct.
> + */
> +static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen,
> char *tag, ID3v2ExtraMeta **extra_meta)
> +{
> + ID3v2ExtraMetaGEOB *geob_data = NULL;
> + ID3v2ExtraMeta *new_extra = NULL;
> + char encoding;
> + unsigned int len;
> +
> + if (taglen < 1)
> + return;
> +
> + geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB));
> + if (!geob_data) {
> + av_log(s, AV_LOG_ERROR, "Failed to alloc %"PRIuPTR" bytes\n",
> sizeof(ID3v2ExtraMetaGEOB));
PRIuPTR? sizeof(foo) should be printfed with %zd IIRC. Same below.
> + return;
> + }
> +
> + new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
> + if (!new_extra) {
> + av_log(s, AV_LOG_ERROR, "Failed to alloc %"PRIuPTR" bytes\n",
> sizeof(ID3v2ExtraMeta));
> + goto fail;
> + }
> +
> + /* read encoding type byte */
> + encoding = avio_r8(pb);
> + taglen--;
> +
> + /* read MIME type (always ISO-8859) */
> + if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &geob_data->mime_type,
> NULL, &taglen, 1) < 0
> + || taglen <= 0)
mimetype/filename/description cannot be of zero length?
--
Anton Khirnov
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel