av_freep(&geob->file_name);
av_freep(&geob->description);
av_freep(&geob->data);
- av_free(geob);
}
/**
{
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;
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);
}
/**
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;
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--;
/* 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;
ID3v2ExtraMetaAPIC *apic = obj;
av_buffer_unref(&apic->buf);
av_freep(&apic->description);
- av_freep(&apic);
}
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};
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;
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;
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];
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;
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);
}
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;
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);
}
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;
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;
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) {
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);
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;
((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);
}
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,
while (current) {
if ((extra_func = get_extra_meta_func(current->tag, 1)))
- extra_func->free(current->data);
+ extra_func->free(¤t->data);
next = current->next;
av_freep(¤t);
current = next;
*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;
}
if ((ret = av_dict_set(metadata, key, escaped, dict_flags)) < 0) {
- av_free(key);
- av_free(escaped);
return ret;
}
}
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);
}