]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/id3v2.c
Merge commit '48bb0da050329e5111b00a12dfc154b7e78fb3a3'
[ffmpeg] / libavformat / id3v2.c
index f15cefee4799cad3cd0cf04479359ab7d39ad1df..6c216ba7a251a7a72c22051cb42603f661d47bce 100644 (file)
@@ -670,59 +670,68 @@ fail:
     avio_seek(pb, end, SEEK_SET);
 }
 
+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)
 {
-    AVRational time_base = {1, 1000};
-    uint32_t start, end;
-    AVChapter *chapter;
-    uint8_t *dst = NULL;
     int taglen;
     char tag[5];
+    ID3v2ExtraMeta *new_extra = NULL;
+    ID3v2ExtraMetaCHAP *chap  = NULL;
 
-    if (!s) {
-        /* We should probably just put the chapter data to extra_meta here
-         * and do the AVFormatContext-needing part in a separate
-         * ff_id3v2_parse_apic()-like function. */
-        av_log(NULL, AV_LOG_DEBUG, "No AVFormatContext, skipped ID3 chapter data\n");
-        return;
-    }
+    new_extra = av_mallocz(sizeof(*new_extra));
+    chap      = av_mallocz(sizeof(*chap));
+
+    if (!new_extra || !chap)
+        goto fail;
+
+    if (decode_str(s, pb, 0, &chap->element_id, &len) < 0)
+        goto fail;
 
-    if (decode_str(s, pb, 0, &dst, &len) < 0)
-        return;
     if (len < 16)
-        return;
+        goto fail;
 
-    start = avio_rb32(pb);
-    end   = avio_rb32(pb);
+    chap->start = avio_rb32(pb);
+    chap->end   = avio_rb32(pb);
     avio_skip(pb, 8);
 
-    chapter = avpriv_new_chapter(s, s->nb_chapters + 1, time_base, start, end, dst);
-    if (!chapter) {
-        av_free(dst);
-        return;
-    }
-
     len -= 16;
     while (len > 10) {
         if (avio_read(pb, tag, 4) < 4)
-            goto end;
+            goto fail;
         tag[4] = 0;
         taglen = avio_rb32(pb);
         avio_skip(pb, 2);
         len -= 10;
         if (taglen < 0 || taglen > len)
-            goto end;
+            goto fail;
         if (tag[0] == 'T')
-            read_ttag(s, pb, taglen, &chapter->metadata, tag);
+            read_ttag(s, pb, taglen, &chap->meta, tag);
         else
             avio_skip(pb, taglen);
         len -= taglen;
     }
 
-    ff_metadata_conv(&chapter->metadata, NULL, ff_id3v2_34_metadata_conv);
-    ff_metadata_conv(&chapter->metadata, NULL, ff_id3v2_4_metadata_conv);
-end:
-    av_free(dst);
+    ff_metadata_conv(&chap->meta, NULL, ff_id3v2_34_metadata_conv);
+    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;
+
+    return;
+
+fail:
+    if (chap)
+        free_chapter(chap);
+    av_freep(&new_extra);
 }
 
 static void free_priv(void *obj)
@@ -782,7 +791,7 @@ typedef struct ID3v2EMFunc {
 static const ID3v2EMFunc id3v2_extra_meta_funcs[] = {
     { "GEO", "GEOB", read_geobtag, free_geobtag },
     { "PIC", "APIC", read_apic,    free_apic    },
-    { "CHAP","CHAP", read_chapter, NULL         },
+    { "CHAP","CHAP", read_chapter, free_chapter },
     { "PRIV","PRIV", read_priv,    free_priv    },
     { NULL }
 };
@@ -1164,3 +1173,54 @@ int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
 
     return 0;
 }
+
+int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta)
+{
+    int ret = 0;
+    ID3v2ExtraMeta *cur;
+    AVRational time_base = {1, 1000};
+    ID3v2ExtraMetaCHAP **chapters = NULL;
+    int num_chapters = 0;
+    int i;
+
+    // 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) {
+        ID3v2ExtraMetaCHAP *chap;
+
+        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);
+        if (!chapter)
+            continue;
+
+        if ((ret = av_dict_copy(&chapter->metadata, chap->meta, 0)) < 0)
+            goto end;
+    }
+
+end:
+    av_freep(&chapters);
+    return ret;
+}