summaryrefslogtreecommitdiff
path: root/libavformat/id3v2enc.c
diff options
context:
space:
mode:
authorRichard Shaffer <rshaffer@tunein.com>2018-01-23 09:39:53 -0800
committerwm4 <nfxjfg@googlemail.com>2018-01-24 04:01:01 +0100
commit8a4cc0a2567fa8418709f75af5539cdf76fefb99 (patch)
tree8b4f3db2013b830a884249f4aced89149eab48fc /libavformat/id3v2enc.c
parentf0320afab977edc7b73317c8ef36ff1d60296401 (diff)
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.c60
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;