summaryrefslogtreecommitdiff
path: root/libavformat/id3v2.c
diff options
context:
space:
mode:
authorLukas Stabe <lukas@stabe.de>2017-10-05 03:34:19 +0200
committerwm4 <nfxjfg@googlemail.com>2017-10-05 17:10:23 +0200
commit1fd80106be3dca9fa0ea13fb364c8d221bd27c15 (patch)
treee820d55b354b49746bbf46f62860de7d20ed8d19 /libavformat/id3v2.c
parentcafd9d66ed9e1bad4ae579a3935f7af57a567a51 (diff)
avformat: fix id3 chapters
These changes store id3 chapter data in ID3v2ExtraMeta and introduce ff_id3v2_parse_chapters to parse them into the format context if needed. Encoders using ff_id3v2_read, which previously parsed chapters into the format context automatically, were adjusted to call ff_id3v2_parse_chapters. Signed-off-by: wm4 <nfxjfg@googlemail.com>
Diffstat (limited to 'libavformat/id3v2.c')
-rw-r--r--libavformat/id3v2.c120
1 files changed, 90 insertions, 30 deletions
diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c
index f15cefee47..6c216ba7a2 100644
--- a/libavformat/id3v2.c
+++ b/libavformat/id3v2.c
@@ -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;
+}