]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/id3v2.c
avfilter/avfilter: Remove compatibility code for old filter options
[ffmpeg] / libavformat / id3v2.c
index b43ab1745f27dae3636eff5fb2592d9eea687240..96226193c1dd74cd3cc7cb41acc87f3d47b89441 100644 (file)
@@ -225,7 +225,6 @@ static void free_geobtag(void *obj)
     av_freep(&geob->file_name);
     av_freep(&geob->description);
     av_freep(&geob->data);
-    av_free(geob);
 }
 
 /**
@@ -361,8 +360,8 @@ static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen,
 {
     uint8_t lang[4];
     uint8_t *descriptor = NULL; // 'Content descriptor'
-    uint8_t *text = NULL;
-    char *key = NULL;
+    uint8_t *text;
+    char *key;
     int encoding;
     int ok = 0;
 
@@ -387,18 +386,19 @@ static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen,
     key = av_asprintf("lyrics-%s%s%s", descriptor[0] ? (char *)descriptor : "",
                                        descriptor[0] ? "-" : "",
                                        lang);
-    if (!key)
+    if (!key) {
+        av_free(text);
         goto error;
+    }
 
-    av_dict_set(metadata, key, text, 0);
+    av_dict_set(metadata, key, text,
+                AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
 
     ok = 1;
 error:
     if (!ok)
         av_log(s, AV_LOG_ERROR, "Error reading lyrics, skipped\n");
     av_free(descriptor);
-    av_free(text);
-    av_free(key);
 }
 
 /**
@@ -443,12 +443,24 @@ static void read_comment(AVFormatContext *s, AVIOContext *pb, int taglen,
         av_dict_set(metadata, key, (const char *) dst, dict_flags);
 }
 
+typedef struct ExtraMetaList {
+    ID3v2ExtraMeta *head, *tail;
+} ExtraMetaList;
+
+static void list_append(ID3v2ExtraMeta *new_elem, ExtraMetaList *list)
+{
+    if (list->tail)
+        list->tail->next = new_elem;
+    else
+        list->head = new_elem;
+    list->tail = new_elem;
+}
+
 /**
  * Parse GEOB tag into a ID3v2ExtraMetaGEOB struct.
  */
 static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen,
-                         const char *tag, ID3v2ExtraMeta **extra_meta,
-                         int isv34)
+                         const char *tag, ExtraMetaList *extra_meta, int isv34)
 {
     ID3v2ExtraMetaGEOB *geob_data = NULL;
     ID3v2ExtraMeta *new_extra     = NULL;
@@ -458,20 +470,15 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen,
     if (taglen < 1)
         return;
 
-    geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB));
-    if (!geob_data) {
-        av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n",
-               sizeof(ID3v2ExtraMetaGEOB));
-        return;
-    }
-
     new_extra = av_mallocz(sizeof(ID3v2ExtraMeta));
     if (!new_extra) {
         av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n",
                sizeof(ID3v2ExtraMeta));
-        goto fail;
+        return;
     }
 
+    geob_data = &new_extra->data.geob;
+
     /* read encoding type byte */
     encoding = avio_r8(pb);
     taglen--;
@@ -510,9 +517,7 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen,
 
     /* add data to the list */
     new_extra->tag  = "GEOB";
-    new_extra->data = geob_data;
-    new_extra->next = *extra_meta;
-    *extra_meta     = new_extra;
+    list_append(new_extra, extra_meta);
 
     return;
 
@@ -576,7 +581,6 @@ static void free_apic(void *obj)
     ID3v2ExtraMetaAPIC *apic = obj;
     av_buffer_unref(&apic->buf);
     av_freep(&apic->description);
-    av_freep(&apic);
 }
 
 static void rstrip_spaces(char *buf)
@@ -587,8 +591,7 @@ static void rstrip_spaces(char *buf)
 }
 
 static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
-                      const char *tag, ID3v2ExtraMeta **extra_meta,
-                      int isv34)
+                      const char *tag, ExtraMetaList *extra_meta, int isv34)
 {
     int enc, pic_type;
     char mimetype[64] = {0};
@@ -602,16 +605,20 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
         goto fail;
 
     new_extra = av_mallocz(sizeof(*new_extra));
-    apic      = av_mallocz(sizeof(*apic));
-    if (!new_extra || !apic)
+    if (!new_extra)
         goto fail;
 
+    apic = &new_extra->data.apic;
+
     enc = avio_r8(pb);
     taglen--;
 
     /* mimetype */
     if (isv34) {
-        taglen -= avio_get_str(pb, taglen, mimetype, sizeof(mimetype));
+        int ret = avio_get_str(pb, taglen, mimetype, sizeof(mimetype));
+        if (ret < 0 || ret >= taglen)
+            goto fail;
+        taglen -= ret;
     } else {
         if (avio_read(pb, mimetype, 3) < 0)
             goto fail;
@@ -657,13 +664,11 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen,
     memset(apic->buf->data + taglen, 0, AV_INPUT_BUFFER_PADDING_SIZE);
 
     new_extra->tag  = "APIC";
-    new_extra->data = apic;
-    new_extra->next = *extra_meta;
-    *extra_meta     = new_extra;
 
     // The description must be unique, and some ID3v2 tag writers add spaces
     // to write several APIC entries with the same description.
     rstrip_spaces(apic->description);
+    list_append(new_extra, extra_meta);
 
     return;
 
@@ -679,10 +684,10 @@ static void free_chapter(void *obj)
     ID3v2ExtraMetaCHAP *chap = obj;
     av_freep(&chap->element_id);
     av_dict_free(&chap->meta);
-    av_freep(&chap);
 }
 
-static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const char *ttag, ID3v2ExtraMeta **extra_meta, int isv34)
+static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len,
+                         const char *ttag, ExtraMetaList *extra_meta, int isv34)
 {
     int taglen;
     char tag[5];
@@ -690,10 +695,10 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const cha
     ID3v2ExtraMetaCHAP *chap  = NULL;
 
     new_extra = av_mallocz(sizeof(*new_extra));
-    chap      = av_mallocz(sizeof(*chap));
+    if (!new_extra)
+        return;
 
-    if (!new_extra || !chap)
-        goto fail;
+    chap = &new_extra->data.chap;
 
     if (decode_str(s, pb, 0, &chap->element_id, &len) < 0)
         goto fail;
@@ -726,15 +731,12 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const cha
     ff_metadata_conv(&chap->meta, NULL, ff_id3v2_4_metadata_conv);
 
     new_extra->tag  = "CHAP";
-    new_extra->data = chap;
-    new_extra->next = *extra_meta;
-    *extra_meta     = new_extra;
+    list_append(new_extra, extra_meta);
 
     return;
 
 fail:
-    if (chap)
-        free_chapter(chap);
+    free_chapter(chap);
     av_freep(&new_extra);
 }
 
@@ -743,20 +745,19 @@ static void free_priv(void *obj)
     ID3v2ExtraMetaPRIV *priv = obj;
     av_freep(&priv->owner);
     av_freep(&priv->data);
-    av_freep(&priv);
 }
 
 static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen,
-                      const char *tag, ID3v2ExtraMeta **extra_meta, int isv34)
+                      const char *tag, ExtraMetaList *extra_meta, int isv34)
 {
     ID3v2ExtraMeta *meta;
     ID3v2ExtraMetaPRIV *priv;
 
     meta = av_mallocz(sizeof(*meta));
-    priv = av_mallocz(sizeof(*priv));
+    if (!meta)
+        return;
 
-    if (!meta || !priv)
-        goto fail;
+    priv = &meta->data.priv;
 
     if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &priv->owner, &taglen) < 0)
         goto fail;
@@ -771,15 +772,12 @@ static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen,
         goto fail;
 
     meta->tag   = "PRIV";
-    meta->data  = priv;
-    meta->next  = *extra_meta;
-    *extra_meta = meta;
+    list_append(meta, extra_meta);
 
     return;
 
 fail:
-    if (priv)
-        free_priv(priv);
+    free_priv(priv);
     av_freep(&meta);
 }
 
@@ -787,7 +785,7 @@ typedef struct ID3v2EMFunc {
     const char *tag3;
     const char *tag4;
     void (*read)(AVFormatContext *s, AVIOContext *pb, int taglen,
-                 const char *tag, ID3v2ExtraMeta **extra_meta,
+                 const char *tag, ExtraMetaList *extra_meta,
                  int isv34);
     void (*free)(void *obj);
 } ID3v2EMFunc;
@@ -821,12 +819,12 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
 
 static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
                         AVFormatContext *s, int len, uint8_t version,
-                        uint8_t flags, ID3v2ExtraMeta **extra_meta)
+                        uint8_t flags, ExtraMetaList *extra_meta)
 {
     int isv34, unsync;
     unsigned tlen;
     char tag[5];
-    int64_t next, end = avio_tell(pb) + len;
+    int64_t next, end = avio_tell(pb);
     int taghdrlen;
     const char *reason = NULL;
     AVIOContext pb_local;
@@ -838,6 +836,10 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
     av_unused int uncompressed_buffer_size = 0;
     const char *comm_frame;
 
+    if (end > INT64_MAX - len - 10)
+        return;
+    end += len;
+
     av_log(s, AV_LOG_DEBUG, "id3v2 ver:%d flags:%02X len:%d\n", version, flags, len);
 
     switch (version) {
@@ -1006,6 +1008,11 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
 
                     av_log(s, AV_LOG_DEBUG, "Compresssed frame %s tlen=%d dlen=%ld\n", tag, tlen, dlen);
 
+                    if (tlen <= 0)
+                        goto seek;
+                    if (dlen / 32768 > tlen)
+                        goto seek;
+
                     av_fast_malloc(&uncompressed_buffer, &uncompressed_buffer_size, dlen);
                     if (!uncompressed_buffer) {
                         av_log(s, AV_LOG_ERROR, "Failed to alloc %ld bytes\n", dlen);
@@ -1068,13 +1075,17 @@ error:
 
 static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata,
                                 AVFormatContext *s, const char *magic,
-                                ID3v2ExtraMeta **extra_meta, int64_t max_search_size)
+                                ID3v2ExtraMeta **extra_metap, int64_t max_search_size)
 {
     int len, ret;
     uint8_t buf[ID3v2_HEADER_SIZE];
+    ExtraMetaList extra_meta = { NULL };
     int found_header;
     int64_t start, off;
 
+    if (extra_metap)
+        *extra_metap = NULL;
+
     if (max_search_size && max_search_size < ID3v2_HEADER_SIZE)
         return;
 
@@ -1101,7 +1112,8 @@ static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata,
                   ((buf[7] & 0x7f) << 14) |
                   ((buf[8] & 0x7f) << 7) |
                    (buf[9] & 0x7f);
-            id3v2_parse(pb, metadata, s, len, buf[3], buf[5], extra_meta);
+            id3v2_parse(pb, metadata, s, len, buf[3], buf[5],
+                        extra_metap ? &extra_meta : NULL);
         } else {
             avio_seek(pb, off, SEEK_SET);
         }
@@ -1110,6 +1122,8 @@ static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata,
     ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv);
     ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv);
     merge_date(metadata);
+    if (extra_metap)
+        *extra_metap = extra_meta.head;
 }
 
 void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata,
@@ -1131,7 +1145,7 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
 
     while (current) {
         if ((extra_func = get_extra_meta_func(current->tag, 1)))
-            extra_func->free(current->data);
+            extra_func->free(&current->data);
         next = current->next;
         av_freep(&current);
         current = next;
@@ -1140,105 +1154,70 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
     *extra_meta = NULL;
 }
 
-int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
+int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta *extra_meta)
 {
     ID3v2ExtraMeta *cur;
 
-    for (cur = *extra_meta; cur; cur = cur->next) {
+    for (cur = extra_meta; cur; cur = cur->next) {
         ID3v2ExtraMetaAPIC *apic;
         AVStream *st;
+        int ret;
 
         if (strcmp(cur->tag, "APIC"))
             continue;
-        apic = cur->data;
+        apic = &cur->data.apic;
 
-        if (!(st = avformat_new_stream(s, NULL)))
-            return AVERROR(ENOMEM);
-
-        st->disposition      |= AV_DISPOSITION_ATTACHED_PIC;
-        st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+        ret = ff_add_attached_pic(s, NULL, NULL, &apic->buf, 0);
+        if (ret < 0)
+            return ret;
+        st  = s->streams[s->nb_streams - 1];
         st->codecpar->codec_id   = apic->id;
 
-        if (AV_RB64(apic->buf->data) == PNGSIG)
+        if (AV_RB64(st->attached_pic.data) == PNGSIG)
             st->codecpar->codec_id = AV_CODEC_ID_PNG;
 
         if (apic->description[0])
             av_dict_set(&st->metadata, "title", apic->description, 0);
 
         av_dict_set(&st->metadata, "comment", apic->type, 0);
-
-        av_init_packet(&st->attached_pic);
-        st->attached_pic.buf          = apic->buf;
-        st->attached_pic.data         = apic->buf->data;
-        st->attached_pic.size         = apic->buf->size - AV_INPUT_BUFFER_PADDING_SIZE;
-        st->attached_pic.stream_index = st->index;
-        st->attached_pic.flags       |= AV_PKT_FLAG_KEY;
-
-        apic->buf = NULL;
     }
 
     return 0;
 }
 
-int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
+int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta *cur)
 {
-    int ret = 0;
-    ID3v2ExtraMeta *cur;
     AVRational time_base = {1, 1000};
-    ID3v2ExtraMetaCHAP **chapters = NULL;
-    int num_chapters = 0;
-    int i;
+    int ret;
 
-    // since extra_meta is a linked list where elements are prepended,
-    // we need to reverse the order of chapters
-    for (cur = *extra_meta; cur; cur = cur->next) {
+    for (unsigned i = 0; cur; cur = cur->next) {
         ID3v2ExtraMetaCHAP *chap;
+        AVChapter *chapter;
 
         if (strcmp(cur->tag, "CHAP"))
             continue;
-        chap = cur->data;
-
-        if ((ret = av_dynarray_add_nofree(&chapters, &num_chapters, chap)) < 0)
-            goto end;
-    }
-
-    for (i = 0; i < (num_chapters / 2); i++) {
-        ID3v2ExtraMetaCHAP *right;
-        int right_index;
-
-        right_index = (num_chapters - 1) - i;
-        right = chapters[right_index];
-
-        chapters[right_index] = chapters[i];
-        chapters[i] = right;
-    }
 
-    for (i = 0; i < num_chapters; i++) {
-        ID3v2ExtraMetaCHAP *chap;
-        AVChapter *chapter;
-
-        chap = chapters[i];
-        chapter = avpriv_new_chapter(s, i, time_base, chap->start, chap->end, chap->element_id);
+        chap = &cur->data.chap;
+        chapter = avpriv_new_chapter(s, i++, time_base, chap->start,
+                                     chap->end, chap->element_id);
         if (!chapter)
             continue;
 
         if ((ret = av_dict_copy(&chapter->metadata, chap->meta, 0)) < 0)
-            goto end;
+            return ret;
     }
 
-end:
-    av_freep(&chapters);
-    return ret;
+    return 0;
 }
 
-int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_meta)
+int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta *extra_meta)
 {
     ID3v2ExtraMeta *cur;
     int dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL;
 
-    for (cur = *extra_meta; cur; cur = cur->next) {
+    for (cur = extra_meta; cur; cur = cur->next) {
         if (!strcmp(cur->tag, "PRIV")) {
-            ID3v2ExtraMetaPRIV *priv = cur->data;
+            ID3v2ExtraMetaPRIV *priv = &cur->data.priv;
             AVBPrint bprint;
             char *escaped, *key;
             int i, ret;
@@ -1263,8 +1242,6 @@ int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_met
             }
 
             if ((ret = av_dict_set(metadata, key, escaped, dict_flags)) < 0) {
-                av_free(key);
-                av_free(escaped);
                 return ret;
             }
         }
@@ -1273,7 +1250,7 @@ int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_met
     return 0;
 }
 
-int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
+int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta *extra_meta)
 {
     return ff_id3v2_parse_priv_dict(&s->metadata, extra_meta);
 }