From patchwork Fri Jan 12 21:13:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: rshaffer@tunein.com X-Patchwork-Id: 7268 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.78.2 with SMTP id r2csp2455274jaa; Fri, 12 Jan 2018 13:18:48 -0800 (PST) X-Google-Smtp-Source: ACJfBovn/27tXLuO+vVo/WQmf5CgwO5jLi+475j1XouJ+/XDtRABpepp3G/Lt78jn1SeczWjxcB8 X-Received: by 10.28.198.12 with SMTP id w12mr4940400wmf.75.1515791928047; Fri, 12 Jan 2018 13:18:48 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1515791928; cv=none; d=google.com; s=arc-20160816; b=S3zq0OEv5/MwDIbbspaJaAXcGH6XoVtRJ6APWn49BCq97YoM6ohWLkgpIU+ny5uaVr sg7WCrvLcz2ZXshhwV85aeN4VAmfwpXFioSAxA9loHcO2QiXix9RawmV7t1o8adA7fLf 3yFjxB5RAFeyAVVrklXZuKmL9kBGJPQS4hfaiWU+SER/z1zvHfNE8E27yR5kMJiyLiFX YiDGeccXZrl2ia56YHlhKvXaeULJUsewqoAGDBQt6wGlt+7JySgKW8Ofq188lChOKBVH ChYrS27eYbftudu0eJhka6Rtm89MBcj9evNFqXnVRQmq3MM6U+KUNy7gV3t7Wf75ZJrW lUCg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:message-id:date:to:from:dkim-signature :delivered-to:arc-authentication-results; bh=sotPInvOAAZeGwgz67zUOi5kC0E9OSKwpMgARs87A58=; b=ZWpGmQSXOR6xPuMG5tN6IDtwM6lhvX3fDq4MPtslCmsEkYgr1A0kFz9LNlCoE9zGaG JOgs62vk9Trgr+Tlfsqa4NJNEJFjnvLA20ebMsZR6gxvM4j5cOVEGvUq9LuClME+XDmw csosNjVDEuGEC4vX5DYCUe/pwQ+2OXWxfX6PryVY8HAwyi5pXQTd82sice1m7Fe+FhdT Ty88NZp/paozQ3uNwsJAQNsolA7QX8ntXOQI2RAekD+V0LVBHnF0QxGV2DJfBHP7MQ9N rFk2vU1c9SYiUh5P8mw70J1fznEqlJIFNckpcmPgKcWDfbPmOwayC6rTJlsZbHkv83o1 wLng== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@tunein-com.20150623.gappssmtp.com header.s=20150623 header.b=oC0cJROS; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id m5si16406780wrm.196.2018.01.12.13.18.47; Fri, 12 Jan 2018 13:18:47 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@tunein-com.20150623.gappssmtp.com header.s=20150623 header.b=oC0cJROS; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 5B6A668A209; Fri, 12 Jan 2018 23:18:45 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg0-f50.google.com (mail-pg0-f50.google.com [74.125.83.50]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1833E689E4A for ; Fri, 12 Jan 2018 23:18:39 +0200 (EET) Received: by mail-pg0-f50.google.com with SMTP id j4so5379106pgp.1 for ; Fri, 12 Jan 2018 13:18:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tunein-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id; bh=anGLM2Wn4iGdWwcDymuLqfcF/s/01CmL7zkkppBxqQg=; b=oC0cJROSOotmYJiZf0CsjmfRk/T8CduGm3szbf5XQewLbL+reA6jbiM9ukYYeTXhF/ vz4sZOdWqnXhgycDLdGoZretLt4CXxWOaPLxWb79bEvkTIjBzaVm3Lbsoi8g8KBYlgYu gYKsyjc5qczRSCQQ4crN6rAxSN1ZRjaKn/p6yJxL7DLTzVMDt3L72XQQJLoqphdbLYYx enmFqgmHXvAVMEu9Mw1ZdsUZfO404EJCau7xYXLpYiP6THaLFfqzmPU0LVcQNEH+6Kcc VDWgLS0JHwTIDiVZuByhA+1Ad5zZ/xzl0R9L+chPipgRBFIE+ULN9znc40Jqjw73xP25 IaNQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=anGLM2Wn4iGdWwcDymuLqfcF/s/01CmL7zkkppBxqQg=; b=tOHfvA5VUXImMCeHspmnOXqXCgHFN5rH4fqWrOjlDqapFv6fPlDrYF16m4EnZ2H+zD 0zBhj/GYt664XR5QRZ7Txp/Cyk4TpaBsAUyf8MggX0eWK/dFpnjeRV+Y+0i7VshpXNdC PrK5/70lZ9dAX1ewwsbwElDjKz70P1O7FiXjy4mVBVLiAtIDgkes7aE+apOTO3YsgoVQ kqocQbaFpizHYlV6nOEIPZU79dtBRW7wxSbO7qA1IuHKn39XHylTvxD1386j1rgGOtJl rOY1/VwqBbIBDLdviHgk9sQ53aY/KM7+VDbemJnIi3zORzVxw+A2dZH+cJEUFUdh2kwW UKsg== X-Gm-Message-State: AKwxytdJCkwvBXXZ9OP+EcjotWoTQggZtQJoVHWUw5G9StOliak2b6Ee luWuuQPHsF0+LE2loJx+JOrIPGMr X-Received: by 10.84.235.1 with SMTP id o1mr1452360plk.51.1515791588466; Fri, 12 Jan 2018 13:13:08 -0800 (PST) Received: from 000984.tunein.corp ([199.87.87.163]) by smtp.gmail.com with ESMTPSA id c28sm48996839pfe.69.2018.01.12.13.13.07 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 12 Jan 2018 13:13:07 -0800 (PST) From: rshaffer@tunein.com To: ffmpeg-devel@ffmpeg.org Date: Fri, 12 Jan 2018 13:13:05 -0800 Message-Id: <20180112211305.5812-1-rshaffer@tunein.com> X-Mailer: git-send-email 2.14.3 (Apple Git-98) Subject: [FFmpeg-devel] [PATCH] avformat: add option to parse/store ID3 PRIV tags in metadata. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Richard Shaffer MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Richard Shaffer 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, 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. As this introduces a change in behavior, it must be enabled by setting the 'id3v2_parse_priv' option. --- I want to be able to expose PRIV tags using an existing API, but not sure if this is the best approach. In particular, PRIV data may be of any type, while metadata (and the AVDictionary type it uses) expresses values as strings. Any feedback on the approach or specifics would be much appreciated, especially if there is a suggestion for a better way to accomplish this. -Richard libavformat/aacdec.c | 40 +++++++++++++++++++++++++++++++--------- libavformat/id3v2.c | 40 ++++++++++++++++++++++++++++++++++++++++ libavformat/id3v2.h | 13 +++++++++++++ libavformat/mp3dec.c | 2 ++ libavformat/utils.c | 4 ++++ 5 files changed, 90 insertions(+), 9 deletions(-) diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c index 36d558ff54..46e10f34af 100644 --- a/libavformat/aacdec.c +++ b/libavformat/aacdec.c @@ -21,6 +21,7 @@ */ #include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" #include "avformat.h" #include "internal.h" #include "id3v1.h" @@ -28,6 +29,11 @@ #define ADTS_HEADER_SIZE 7 +typedef struct AACDemuxContext { + AVClass *class; + int id3v2_parse_priv; +} AACDemuxContext; + static int adts_aac_probe(AVProbeData *p) { int max_frames = 0, first_frames = 0; @@ -146,14 +152,30 @@ static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +static const AVOption aac_options[] = { + { "id3v2_parse_priv", + "parse ID3v2 PRIV tags", offsetof(AACDemuxContext, id3v2_parse_priv), + AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass aac_class = { + .class_name = "aac", + .item_name = av_default_item_name, + .option = aac_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVInputFormat ff_aac_demuxer = { - .name = "aac", - .long_name = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"), - .read_probe = adts_aac_probe, - .read_header = adts_aac_read_header, - .read_packet = adts_aac_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "aac", - .mime_type = "audio/aac,audio/aacp,audio/x-aac", - .raw_codec_id = AV_CODEC_ID_AAC, + .name = "aac", + .long_name = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"), + .read_probe = adts_aac_probe, + .read_header = adts_aac_read_header, + .read_packet = adts_aac_read_packet, + .flags = AVFMT_GENERIC_INDEX, + .priv_class = &aac_class, + .priv_data_size = sizeof(AACDemuxContext), + .extensions = "aac", + .mime_type = "audio/aac,audio/aacp,audio/x-aac", + .raw_codec_id = AV_CODEC_ID_AAC, }; diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index 6c216ba7a2..dd151dd7f2 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -33,6 +33,7 @@ #endif #include "libavutil/avstring.h" +#include "libavutil/bprint.h" #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" #include "avio_internal.h" @@ -1224,3 +1225,42 @@ end: av_freep(&chapters); return ret; } + +int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_meta) +{ + ID3v2ExtraMeta *cur; + int dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_VAL; + + for (cur = *extra_meta; cur; cur = cur->next) { + if (!strcmp(cur->tag, "PRIV")) { + ID3v2ExtraMetaPRIV *priv = cur->data; + AVBPrint bprint; + char * escaped; + int i, ret; + + av_bprint_init(&bprint, priv->datasize + sizeof(char), AV_BPRINT_SIZE_UNLIMITED); + + for (i = 0; i < priv->datasize; i++) { + if (priv->data[i] < 32 || priv->data[i] > 126) { + av_bprintf(&bprint, "\\x%02x", priv->data[i]); + } else if (priv->data[i] == '\\') { + av_bprint_chars(&bprint, '\\', 2); + } else { + av_bprint_chars(&bprint, priv->data[i], 1); + } + } + + if ((ret = av_bprint_finalize(&bprint, &escaped)) < 0) + return ret; + + av_dict_set(metadata, priv->owner, escaped, dict_flags); + } + } + + return 0; +} + +int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) +{ + return ff_id3v2_parse_priv_dict(&s->metadata, extra_meta); +} \ No newline at end of file diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h index 5e64ead096..5f46a46115 100644 --- a/libavformat/id3v2.h +++ b/libavformat/id3v2.h @@ -167,6 +167,19 @@ int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta); */ int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta); +/** + * Parse PRIV tags into a dictionary. The PRIV owner is the metadata key. The + * PRIV data is the value, with non-printable characters escaped. + */ +int ff_id3v2_parse_priv_dict(AVDictionary **d, ID3v2ExtraMeta **extra_meta); + +/** + * Add metadata for all PRIV tags in the ID3v2 header. The PRIV owner is the + * metadata key. The PRIV data is the value, with non-printable characters + * escaped. + */ +int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta **extra_meta); + extern const AVMetadataConv ff_id3v2_34_metadata_conv[]; extern const AVMetadataConv ff_id3v2_4_metadata_conv[]; diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c index a76fe32e59..d2041d0c44 100644 --- a/libavformat/mp3dec.c +++ b/libavformat/mp3dec.c @@ -55,6 +55,7 @@ typedef struct { unsigned frames; /* Total number of frames in file */ unsigned header_filesize; /* Total number of bytes in the stream */ int is_cbr; + int id3v2_parse_priv; } MP3DecContext; enum CheckRet { @@ -579,6 +580,7 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, static const AVOption options[] = { { "usetoc", "use table of contents", offsetof(MP3DecContext, usetoc), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM}, + { "id3v2_parse_priv", "parse ID3v2 PRIV tags", offsetof(MP3DecContext, id3v2_parse_priv), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, { NULL }, }; diff --git a/libavformat/utils.c b/libavformat/utils.c index 2185a6f05b..207628161e 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -629,10 +629,14 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, if (id3v2_extra_meta) { if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") || !strcmp(s->iformat->name, "tta")) { + int64_t id3v2_parse_priv = 0; + av_opt_get_int(s, "id3v2_parse_priv", AV_OPT_SEARCH_CHILDREN, &id3v2_parse_priv); if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) goto fail; if ((ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0) goto fail; + if (id3v2_parse_priv && (ret = ff_id3v2_parse_priv(s, &id3v2_extra_meta)) < 0) + goto fail; } else av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n"); }