This is needed for upcoming ID3v2.3 muxing support. Signed-off-by: Janne Grunau <janne-ffmpeg@jannau.net>tags/n0.8
| @@ -273,46 +273,67 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic) | |||||
| url_fseek(s->pb, off, SEEK_SET); | url_fseek(s->pb, off, SEEK_SET); | ||||
| } | } | ||||
| } while (found_header); | } while (found_header); | ||||
| ff_metadata_conv(&s->metadata, NULL, ff_id3v2_metadata_conv); | |||||
| ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv); | |||||
| ff_metadata_conv(&s->metadata, NULL, ff_id3v2_2_metadata_conv); | |||||
| ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv); | |||||
| } | } | ||||
| const AVMetadataConv ff_id3v2_metadata_conv[] = { | |||||
| const AVMetadataConv ff_id3v2_34_metadata_conv[] = { | |||||
| { "TALB", "album"}, | { "TALB", "album"}, | ||||
| { "TAL", "album"}, | |||||
| { "TCOM", "composer"}, | { "TCOM", "composer"}, | ||||
| { "TCON", "genre"}, | { "TCON", "genre"}, | ||||
| { "TCO", "genre"}, | |||||
| { "TCOP", "copyright"}, | { "TCOP", "copyright"}, | ||||
| { "TDRL", "date"}, | |||||
| { "TDRC", "date"}, | |||||
| { "TDEN", "creation_time"}, | |||||
| { "TENC", "encoded_by"}, | { "TENC", "encoded_by"}, | ||||
| { "TEN", "encoded_by"}, | |||||
| { "TIT2", "title"}, | { "TIT2", "title"}, | ||||
| { "TT2", "title"}, | |||||
| { "TLAN", "language"}, | { "TLAN", "language"}, | ||||
| { "TPE1", "artist"}, | { "TPE1", "artist"}, | ||||
| { "TP1", "artist"}, | |||||
| { "TPE2", "album_artist"}, | { "TPE2", "album_artist"}, | ||||
| { "TP2", "album_artist"}, | |||||
| { "TPE3", "performer"}, | { "TPE3", "performer"}, | ||||
| { "TP3", "performer"}, | |||||
| { "TPOS", "disc"}, | { "TPOS", "disc"}, | ||||
| { "TPUB", "publisher"}, | { "TPUB", "publisher"}, | ||||
| { "TRCK", "track"}, | { "TRCK", "track"}, | ||||
| { "TRK", "track"}, | |||||
| { "TSSE", "encoder"}, | |||||
| { 0 } | |||||
| }; | |||||
| const AVMetadataConv ff_id3v2_4_metadata_conv[] = { | |||||
| { "TDRL", "date"}, | |||||
| { "TDRC", "date"}, | |||||
| { "TDEN", "creation_time"}, | |||||
| { "TSOA", "album-sort"}, | { "TSOA", "album-sort"}, | ||||
| { "TSOP", "artist-sort"}, | { "TSOP", "artist-sort"}, | ||||
| { "TSOT", "title-sort"}, | { "TSOT", "title-sort"}, | ||||
| { "TSSE", "encoder"}, | |||||
| { 0 } | { 0 } | ||||
| }; | }; | ||||
| const AVMetadataConv ff_id3v2_2_metadata_conv[] = { | |||||
| { "TAL", "album"}, | |||||
| { "TCO", "genre"}, | |||||
| { "TT2", "title"}, | |||||
| { "TEN", "encoded_by"}, | |||||
| { "TP1", "artist"}, | |||||
| { "TP2", "album_artist"}, | |||||
| { "TP3", "performer"}, | |||||
| { "TRK", "track"}, | |||||
| { 0 } | |||||
| }; | |||||
| const char ff_id3v2_tags[][4] = { | const char ff_id3v2_tags[][4] = { | ||||
| "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDEN", "TDLY", "TDOR", "TDRC", | |||||
| "TDRL", "TDTG", "TENC", "TEXT", "TFLT", "TIPL", "TIT1", "TIT2", "TIT3", | |||||
| "TKEY", "TLAN", "TLEN", "TMCL", "TMED", "TMOO", "TOAL", "TOFN", "TOLY", | |||||
| "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", "TPE4", "TPOS", "TPRO", "TPUB", | |||||
| "TRCK", "TRSN", "TRSO", "TSOA", "TSOP", "TSOT", "TSRC", "TSSE", "TSST", | |||||
| "TALB", "TBPM", "TCOM", "TCON", "TCOP", "TDLY", "TENC", "TEXT", | |||||
| "TFLT", "TIT1", "TIT2", "TIT3", "TKEY", "TLAN", "TLEN", "TMED", | |||||
| "TOAL", "TOFN", "TOLY", "TOPE", "TOWN", "TPE1", "TPE2", "TPE3", | |||||
| "TPE4", "TPOS", "TPUB", "TRCK", "TRSN", "TRSO", "TSRC", "TSSE", | |||||
| { 0 }, | |||||
| }; | |||||
| const char ff_id3v2_4_tags[][4] = { | |||||
| "TDEN", "TDOR", "TDRC", "TDRL", "TDTG", "TIPL", "TMCL", "TMOO", | |||||
| "TPRO", "TSOA", "TSOP", "TSOT", "TSST", | |||||
| { 0 }, | |||||
| }; | |||||
| const char ff_id3v2_3_tags[][4] = { | |||||
| "TDAT", "TIME", "TORY", "TRDA", "TSIZ", "TYER", | |||||
| { 0 }, | { 0 }, | ||||
| }; | }; | ||||
| @@ -65,12 +65,25 @@ int ff_id3v2_tag_len(const uint8_t *buf); | |||||
| */ | */ | ||||
| void ff_id3v2_read(AVFormatContext *s, const char *magic); | void ff_id3v2_read(AVFormatContext *s, const char *magic); | ||||
| extern const AVMetadataConv ff_id3v2_metadata_conv[]; | |||||
| extern const AVMetadataConv ff_id3v2_34_metadata_conv[]; | |||||
| extern const AVMetadataConv ff_id3v2_4_metadata_conv[]; | |||||
| extern const AVMetadataConv ff_id3v2_2_metadata_conv[]; | |||||
| /** | /** | ||||
| * A list of ID3v2.4 text information frames. | |||||
| * A list of text information frames allowed in both ID3 v2.3 and v2.4 | |||||
| * http://www.id3.org/id3v2.4.0-frames | * http://www.id3.org/id3v2.4.0-frames | ||||
| * http://www.id3.org/id3v2.4.0-changes | |||||
| */ | */ | ||||
| extern const char ff_id3v2_tags[][4]; | extern const char ff_id3v2_tags[][4]; | ||||
| /** | |||||
| * ID3v2.4-only text information frames. | |||||
| */ | |||||
| extern const char ff_id3v2_4_tags[][4]; | |||||
| /** | |||||
| * ID3v2.3-only text information frames. | |||||
| */ | |||||
| extern const char ff_id3v2_3_tags[][4]; | |||||
| #endif /* AVFORMAT_ID3V2_H */ | #endif /* AVFORMAT_ID3V2_H */ | ||||
| @@ -148,6 +148,20 @@ AVOutputFormat mp2_muxer = { | |||||
| #endif | #endif | ||||
| #if CONFIG_MP3_MUXER | #if CONFIG_MP3_MUXER | ||||
| static int id3v2_check_write_tag(AVFormatContext *s, AVMetadataTag *t, const char table[][4]) | |||||
| { | |||||
| uint32_t tag; | |||||
| int i; | |||||
| if (t->key[0] != 'T' || strlen(t->key) != 4) | |||||
| return -1; | |||||
| 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, ID3v2_ENCODING_UTF8); | |||||
| return -1; | |||||
| } | |||||
| /** | /** | ||||
| * Write an ID3v2.4 header at beginning of stream | * Write an ID3v2.4 header at beginning of stream | ||||
| */ | */ | ||||
| @@ -166,29 +180,25 @@ static int mp3_write_header(struct AVFormatContext *s) | |||||
| size_pos = url_ftell(s->pb); | size_pos = url_ftell(s->pb); | ||||
| put_be32(s->pb, 0); | put_be32(s->pb, 0); | ||||
| ff_metadata_conv(&s->metadata, ff_id3v2_metadata_conv, NULL); | |||||
| ff_metadata_conv(&s->metadata, ff_id3v2_34_metadata_conv, NULL); | |||||
| ff_metadata_conv(&s->metadata, ff_id3v2_4_metadata_conv, NULL); | |||||
| while ((t = av_metadata_get(s->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) { | while ((t = av_metadata_get(s->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) { | ||||
| uint32_t tag = 0; | |||||
| int ret; | int ret; | ||||
| if (t->key[0] == 'T' && strlen(t->key) == 4) { | |||||
| int i; | |||||
| for (i = 0; *ff_id3v2_tags[i]; i++) | |||||
| if (AV_RB32(t->key) == AV_RB32(ff_id3v2_tags[i])) { | |||||
| tag = AV_RB32(t->key); | |||||
| if ((ret = id3v2_put_ttag(s, t->value, NULL, tag, ID3v2_ENCODING_UTF8)) < 0) | |||||
| return ret; | |||||
| totlen += ret; | |||||
| break; | |||||
| } | |||||
| if ((ret = id3v2_check_write_tag(s, t, ff_id3v2_tags)) > 0) { | |||||
| totlen += ret; | |||||
| continue; | |||||
| } | } | ||||
| if (!tag) { /* unknown tag, write as TXXX frame */ | |||||
| tag = MKBETAG('T', 'X', 'X', 'X'); | |||||
| if ((ret = id3v2_put_ttag(s, t->key, t->value, tag, ID3v2_ENCODING_UTF8)) < 0) | |||||
| return ret; | |||||
| if ((ret = id3v2_check_write_tag(s, t, ff_id3v2_4_tags)) > 0) { | |||||
| totlen += ret; | totlen += ret; | ||||
| continue; | |||||
| } | } | ||||
| /* unknown tag, write as TXXX frame */ | |||||
| if ((ret = id3v2_put_ttag(s, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), | |||||
| ID3v2_ENCODING_UTF8)) < 0) | |||||
| return ret; | |||||
| totlen += ret; | |||||
| } | } | ||||
| cur_pos = url_ftell(s->pb); | cur_pos = url_ftell(s->pb); | ||||