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

Reply via email to