From 1fd80106be3dca9fa0ea13fb364c8d221bd27c15 Mon Sep 17 00:00:00 2001 From: Lukas Stabe Date: Thu, 5 Oct 2017 03:34:19 +0200 Subject: 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 --- libavformat/id3v2.c | 120 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 30 deletions(-) (limited to 'libavformat/id3v2.c') 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; +} -- cgit v1.2.3