From c19981774880919c7f9417014bdcb1fb63f69231 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 27 Feb 2012 21:48:37 +0100 Subject: id3v2enc: make id3v2_put_size take only an AVIOContext. It has no need of full AVFormatContext. --- libavformat/id3v2enc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'libavformat/id3v2enc.c') diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index 36c73bfecb..3a4d229232 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -26,12 +26,12 @@ #include "avio.h" #include "id3v2.h" -static void id3v2_put_size(AVFormatContext *s, int size) +static void id3v2_put_size(AVIOContext *pb, int size) { - avio_w8(s->pb, size >> 21 & 0x7f); - avio_w8(s->pb, size >> 14 & 0x7f); - avio_w8(s->pb, size >> 7 & 0x7f); - avio_w8(s->pb, size & 0x7f); + avio_w8(pb, size >> 21 & 0x7f); + avio_w8(pb, size >> 14 & 0x7f); + avio_w8(pb, size >> 7 & 0x7f); + avio_w8(pb, size & 0x7f); } static int string_is_ascii(const uint8_t *str) @@ -74,7 +74,7 @@ static int id3v2_put_ttag(AVFormatContext *s, const char *str1, const char *str2 len = avio_close_dyn_buf(dyn_buf, &pb); avio_wb32(s->pb, tag); - id3v2_put_size(s, len); + id3v2_put_size(s->pb, len); avio_wb16(s->pb, 0); avio_write(s->pb, pb, len); @@ -140,7 +140,7 @@ int ff_id3v2_write(struct AVFormatContext *s, int id3v2_version, cur_pos = avio_tell(s->pb); avio_seek(s->pb, size_pos, SEEK_SET); - id3v2_put_size(s, totlen); + id3v2_put_size(s->pb, totlen); avio_seek(s->pb, cur_pos, SEEK_SET); return 0; } -- cgit v1.2.3 From 411225aabce57411d1544a7bbc6f6bee6d8ef638 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 27 Feb 2012 22:08:50 +0100 Subject: id3v2enc: split ff_id3v2_write(). This will allow writing the tag in several steps, needed for writing attached pictures. --- libavformat/id3v2enc.c | 64 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 22 deletions(-) (limited to 'libavformat/id3v2enc.c') diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index 3a4d229232..8666818128 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -97,50 +97,70 @@ static int id3v2_check_write_tag(AVFormatContext *s, AVDictionaryEntry *t, const return -1; } -int ff_id3v2_write(struct AVFormatContext *s, int id3v2_version, - const char *magic) +void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version, + const char *magic) { - int64_t size_pos, cur_pos; - AVDictionaryEntry *t = NULL; - - int totlen = 0, enc = id3v2_version == 3 ? ID3v2_ENCODING_UTF16BOM : - ID3v2_ENCODING_UTF8; + id3->version = id3v2_version; - - avio_wb32(s->pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version)); - avio_w8(s->pb, 0); - avio_w8(s->pb, 0); /* flags */ + avio_wb32(pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version)); + avio_w8(pb, 0); + avio_w8(pb, 0); /* flags */ /* reserve space for size */ - size_pos = avio_tell(s->pb); - avio_wb32(s->pb, 0); + id3->size_pos = avio_tell(pb); + avio_wb32(pb, 0); +} + +int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3) +{ + AVDictionaryEntry *t = NULL; + int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM : + ID3v2_ENCODING_UTF8; ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL); - if (id3v2_version == 4) + if (id3->version == 4) ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL); while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { int ret; if ((ret = id3v2_check_write_tag(s, t, ff_id3v2_tags, enc)) > 0) { - totlen += ret; + id3->len += ret; continue; } - if ((ret = id3v2_check_write_tag(s, t, id3v2_version == 3 ? + if ((ret = id3v2_check_write_tag(s, t, id3->version == 3 ? ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) { - totlen += ret; + id3->len += ret; continue; } /* unknown tag, write as TXXX frame */ if ((ret = id3v2_put_ttag(s, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) return ret; - totlen += ret; + id3->len += ret; } - cur_pos = avio_tell(s->pb); - avio_seek(s->pb, size_pos, SEEK_SET); - id3v2_put_size(s->pb, totlen); - avio_seek(s->pb, cur_pos, SEEK_SET); + return 0; +} + +void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb) +{ + int64_t cur_pos = avio_tell(pb); + avio_seek(pb, id3->size_pos, SEEK_SET); + id3v2_put_size(pb, id3->len); + avio_seek(pb, cur_pos, SEEK_SET); +} + +int ff_id3v2_write_simple(struct AVFormatContext *s, int id3v2_version, + const char *magic) +{ + ID3v2EncContext id3 = { 0 }; + int ret; + + ff_id3v2_start(&id3, s->pb, id3v2_version, magic); + if ((ret = ff_id3v2_write_metadata(s, &id3)) < 0) + return ret; + ff_id3v2_finish(&id3, s->pb); + return 0; } -- cgit v1.2.3 From 24fe1a3b1662652df076804fdbfd8b2fd089497e Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Tue, 28 Feb 2012 11:45:07 +0100 Subject: id3v2enc: fix writing frame sizes for ID3v2.3 Frame sizes in ID3v2.3 are not synchsafe, they are simply 32be numbers. In practice this bug is not noticeable unless the frame size takes more than 7 bits (which is almost never for text frames). --- libavformat/id3v2enc.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'libavformat/id3v2enc.c') diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index 8666818128..58f77970ef 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -45,7 +45,7 @@ static int string_is_ascii(const uint8_t *str) * according to encoding (only UTF-8 or UTF-16+BOM supported). * @return number of bytes written or a negative error code. */ -static int id3v2_put_ttag(AVFormatContext *s, const char *str1, const char *str2, +static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char *str1, const char *str2, uint32_t tag, enum ID3v2Encoding enc) { int len; @@ -73,17 +73,21 @@ static int id3v2_put_ttag(AVFormatContext *s, const char *str1, const char *str2 put(dyn_buf, str2); len = avio_close_dyn_buf(dyn_buf, &pb); - avio_wb32(s->pb, tag); - id3v2_put_size(s->pb, len); - avio_wb16(s->pb, 0); - avio_write(s->pb, pb, len); + avio_wb32(avioc, tag); + /* ID3v2.3 frame size is not synchsafe */ + if (id3->version == 3) + avio_wb32(avioc, len); + else + id3v2_put_size(avioc, len); + avio_wb16(avioc, 0); + avio_write(avioc, pb, len); av_freep(&pb); return len + ID3v2_HEADER_SIZE; } -static int id3v2_check_write_tag(AVFormatContext *s, AVDictionaryEntry *t, const char table[][4], - enum ID3v2Encoding enc) +static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, AVDictionaryEntry *t, + const char table[][4], enum ID3v2Encoding enc) { uint32_t tag; int i; @@ -93,7 +97,7 @@ static int id3v2_check_write_tag(AVFormatContext *s, AVDictionaryEntry *t, const tag = AV_RB32(t->key); for (i = 0; *table[i]; i++) if (tag == AV_RB32(table[i])) - return id3v2_put_ttag(s, t->value, NULL, tag, enc); + return id3v2_put_ttag(id3, pb, t->value, NULL, tag, enc); return -1; } @@ -124,18 +128,18 @@ int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3) while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) { int ret; - if ((ret = id3v2_check_write_tag(s, t, ff_id3v2_tags, enc)) > 0) { + if ((ret = id3v2_check_write_tag(id3, s->pb, t, ff_id3v2_tags, enc)) > 0) { id3->len += ret; continue; } - if ((ret = id3v2_check_write_tag(s, t, id3->version == 3 ? + if ((ret = id3v2_check_write_tag(id3, s->pb, t, id3->version == 3 ? ff_id3v2_3_tags : ff_id3v2_4_tags, enc)) > 0) { id3->len += ret; continue; } /* unknown tag, write as TXXX frame */ - if ((ret = id3v2_put_ttag(s, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) + if ((ret = id3v2_put_ttag(id3, s->pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) return ret; id3->len += ret; } -- cgit v1.2.3 From ba445f5557bf16aeb807bb6c83d7de264f98f375 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 27 Feb 2012 22:51:28 +0100 Subject: id3v2enc: add a function for writing attached pictures. Unused so far. --- libavformat/id3v2enc.c | 91 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 9 deletions(-) (limited to 'libavformat/id3v2enc.c') diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index 58f77970ef..624333609b 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -19,6 +19,7 @@ */ #include +#include #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" @@ -40,6 +41,20 @@ static int string_is_ascii(const uint8_t *str) return !*str; } +static void id3v2_encode_string(AVIOContext *pb, const uint8_t *str, + enum ID3v2Encoding enc) +{ + int (*put)(AVIOContext*, const char*); + + if (enc == ID3v2_ENCODING_UTF16BOM) { + avio_wl16(pb, 0xFEFF); /* BOM */ + put = avio_put_str16le; + } else + put = avio_put_str; + + put(pb, str); +} + /** * Write a text frame with one (normal frames) or two (TXXX frames) strings * according to encoding (only UTF-8 or UTF-16+BOM supported). @@ -50,7 +65,6 @@ static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char * { int len; uint8_t *pb; - int (*put)(AVIOContext*, const char*); AVIOContext *dyn_buf; if (avio_open_dyn_buf(&dyn_buf) < 0) return AVERROR(ENOMEM); @@ -62,15 +76,9 @@ static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char * enc = ID3v2_ENCODING_ISO8859; avio_w8(dyn_buf, enc); - if (enc == ID3v2_ENCODING_UTF16BOM) { - avio_wl16(dyn_buf, 0xFEFF); /* BOM */ - put = avio_put_str16le; - } else - put = avio_put_str; - - put(dyn_buf, str1); + id3v2_encode_string(dyn_buf, str1, enc); if (str2) - put(dyn_buf, str2); + id3v2_encode_string(dyn_buf, str2, enc); len = avio_close_dyn_buf(dyn_buf, &pb); avio_wb32(avioc, tag); @@ -147,6 +155,71 @@ int ff_id3v2_write_metadata(AVFormatContext *s, ID3v2EncContext *id3) return 0; } +int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) +{ + AVStream *st = s->streams[pkt->stream_index]; + AVDictionaryEntry *e; + + AVIOContext *dyn_buf; + uint8_t *buf; + const CodecMime *mime = ff_id3v2_mime_tags; + const char *mimetype = NULL, *desc = ""; + int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM : + ID3v2_ENCODING_UTF8; + int i, len, type = 0; + + /* get the mimetype*/ + while (mime->id != CODEC_ID_NONE) { + if (mime->id == st->codec->codec_id) { + mimetype = mime->str; + break; + } + mime++; + } + if (!mimetype) { + av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot " + "write an attached picture.\n", st->index); + return AVERROR(EINVAL); + } + + /* get the picture type */ + e = av_dict_get(st->metadata, "comment", NULL, 0); + for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) { + if (strstr(ff_id3v2_picture_types[i], e->value) == ff_id3v2_picture_types[i]) { + type = i; + break; + } + } + + /* get the description */ + if ((e = av_dict_get(st->metadata, "title", NULL, 0))) + desc = e->value; + + /* start writing */ + if (avio_open_dyn_buf(&dyn_buf) < 0) + return AVERROR(ENOMEM); + + avio_w8(dyn_buf, enc); + avio_put_str(dyn_buf, mimetype); + avio_w8(dyn_buf, type); + id3v2_encode_string(dyn_buf, desc, enc); + avio_write(dyn_buf, pkt->data, pkt->size); + len = avio_close_dyn_buf(dyn_buf, &buf); + + avio_wb32(s->pb, MKBETAG('A', 'P', 'I', 'C')); + if (id3->version == 3) + avio_wb32(s->pb, len); + else + id3v2_put_size(s->pb, len); + avio_wb16(s->pb, 0); + avio_write(s->pb, buf, len); + av_freep(&buf); + + id3->len += len + ID3v2_HEADER_SIZE; + + return 0; +} + void ff_id3v2_finish(ID3v2EncContext *id3, AVIOContext *pb) { int64_t cur_pos = avio_tell(pb); -- cgit v1.2.3