diff options
author | Richard Shaffer <rshaffer@tunein.com> | 2018-01-23 09:39:53 -0800 |
---|---|---|
committer | wm4 <nfxjfg@googlemail.com> | 2018-01-24 04:01:01 +0100 |
commit | 8a4cc0a2567fa8418709f75af5539cdf76fefb99 (patch) | |
tree | 8b4f3db2013b830a884249f4aced89149eab48fc /libavformat/id3v2enc.c | |
parent | f0320afab977edc7b73317c8ef36ff1d60296401 (diff) | |
download | ffmpeg-8a4cc0a2567fa8418709f75af5539cdf76fefb99.tar.gz |
avformat: add option to parse/store ID3 PRIV tags in metadata.
Enables getting access to ID3 PRIV tags from the command-line or metadata API
when demuxing. The PRIV owner is stored as the metadata key prepended with
"id3v2_priv.", and the data is stored as the metadata value. As PRIV tags may
contain arbitrary data, non-printable characters, including NULL bytes, are
escaped as \xXX.
Similarly, any metadata tags that begin with "id3v2_priv." are inserted as ID3
PRIV tags into the output (assuming the format supports ID3). \xXX sequences in
the value are un-escaped to their byte value.
Signed-off-by: wm4 <nfxjfg@googlemail.com>
Diffstat (limited to 'libavformat/id3v2enc.c')
-rw-r--r-- | libavformat/id3v2enc.c | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index 14de76ac06..ffe358f019 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -96,6 +96,59 @@ static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char * return len + ID3v2_HEADER_SIZE; } +/** + * Write a priv frame with owner and data. 'key' is the owner prepended with + * ID3v2_PRIV_METADATA_PREFIX. 'data' is provided as a string. Any \xXX + * (where 'X' is a valid hex digit) will be unescaped to the byte value. + */ +static int id3v2_put_priv(ID3v2EncContext *id3, AVIOContext *avioc, const char *key, const char *data) +{ + int len; + uint8_t *pb; + AVIOContext *dyn_buf; + + if (!av_strstart(key, ID3v2_PRIV_METADATA_PREFIX, &key)) { + return 0; + } + + if (avio_open_dyn_buf(&dyn_buf) < 0) + return AVERROR(ENOMEM); + + // owner + null byte. + avio_write(dyn_buf, key, strlen(key) + 1); + + while (*data) { + if (av_strstart(data, "\\x", &data)) { + if (data[0] && data[1] && av_isxdigit(data[0]) && av_isxdigit(data[1])) { + char digits[] = {data[0], data[1], 0}; + avio_w8(dyn_buf, strtol(digits, NULL, 16)); + data += 2; + } else { + ffio_free_dyn_buf(&dyn_buf); + av_log(avioc, AV_LOG_ERROR, "Invalid escape '\\x%.2s' in metadata tag '" + ID3v2_PRIV_METADATA_PREFIX "%s'.\n", data, key); + return AVERROR(EINVAL); + } + } else { + avio_write(dyn_buf, data++, 1); + } + } + + len = avio_close_dyn_buf(dyn_buf, &pb); + + avio_wb32(avioc, MKBETAG('P', 'R', 'I', 'V')); + if (id3->version == 3) + avio_wb32(avioc, len); + else + id3v2_put_size(avioc, len); + avio_wb16(avioc, 0); + avio_write(avioc, pb, len); + + av_free(pb); + + return len + ID3v2_HEADER_SIZE; +} + static int id3v2_check_write_tag(ID3v2EncContext *id3, AVIOContext *pb, AVDictionaryEntry *t, const char table[][4], enum ID3v2Encoding enc) { @@ -186,6 +239,13 @@ static int write_metadata(AVIOContext *pb, AVDictionary **metadata, continue; } + if ((ret = id3v2_put_priv(id3, pb, t->key, t->value)) > 0) { + id3->len += ret; + continue; + } else if (ret < 0) { + return ret; + } + /* unknown tag, write as TXXX frame */ if ((ret = id3v2_put_ttag(id3, pb, t->key, t->value, MKBETAG('T', 'X', 'X', 'X'), enc)) < 0) return ret; |