ref: 0221ca95fc5829a9fd7d5348507ba2be0dea8264
parent: 2bb99f3cf0119a43884cb7c04fb5dc8c3aec61fc
author: Timothy B. Terriberry <tterribe@xiph.org>
date: Tue Dec 29 13:10:07 EST 2015
Add API to access and preserve binary metadata. This adds support for accessing any binary metadata at the end of the comment header, as first specified in <https://tools.ietf.org/html/draft-ietf-codec-oggopus-05>. It also allows the data to be set and preserves the data when doing deep copies.
--- a/examples/opusfile_example.c
+++ b/examples/opusfile_example.c
@@ -259,6 +259,7 @@
if(li!=prev_li){
const OpusHead *head;
const OpusTags *tags;
+ int binary_suffix_len;
int ci;
/*We found a new link.
Print out some information.*/
@@ -306,6 +307,10 @@
else fprintf(stderr,"<error parsing picture tag>\n");
}
else fprintf(stderr," %s\n",tags->user_comments[ci]);
+ }
+ if(opus_tags_get_binary_suffix(tags,&binary_suffix_len)!=NULL){
+ fprintf(stderr,"<%u bytes of unknown binary metadata>\n",
+ binary_suffix_len);
}
fprintf(stderr,"\n");
if(!op_seekable(of)){
--- a/include/opusfile.h
+++ b/include/opusfile.h
@@ -503,6 +503,22 @@
int opus_tags_add_comment(OpusTags *_tags,const char *_comment)
OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
+/**Replace the binary suffix data at the end of the packet (if any).
+ \param _tags An initialized #OpusTags structure.
+ \param _data A buffer of binary data to append after the encoded user
+ comments.
+ The least significant bit of the first byte of this data must
+ be set (to ensure the data is preserved by other editors).
+ \param _len The number of bytes of binary data to append.
+ This may be zero to remove any existing binary suffix data.
+ \return 0 on success, or a negative value on error.
+ \retval #OP_EINVAL \a _len was negative, or \a _len was positive but
+ \a _data was <code>NULL</code> or the least significant
+ bit of the first byte was not set.
+ \retval #OP_EFAULT An internal memory allocation failed.*/
+int opus_tags_set_binary_suffix(OpusTags *_tags,
+ const unsigned char *_data,int _len) OP_ARG_NONNULL(1);
+
/**Look up a comment value by its tag.
\param _tags An initialized #OpusTags structure.
\param _tag The tag to look up.
@@ -530,6 +546,14 @@
\return The number of instances of this particular tag.*/
int opus_tags_query_count(const OpusTags *_tags,const char *_tag)
OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
+
+/**Retrieve the binary suffix data at the end of the packet (if any).
+ \param _tags An initialized #OpusTags structure.
+ \param[out] _len Returns the number of bytes of binary suffix data returned.
+ \return A pointer to the binary suffix data, or <code>NULL</code> if none
+ was present.*/
+const unsigned char *opus_tags_get_binary_suffix(const OpusTags *_tags,
+ int *_len) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
/**Get the album gain from an R128_ALBUM_GAIN tag, if one was specified.
This searches for the first R128_ALBUM_GAIN tag with a valid signed,
--- a/src/info.c
+++ b/src/info.c
@@ -92,8 +92,11 @@
}
void opus_tags_clear(OpusTags *_tags){
+ int ncomments;
int ci;
- for(ci=_tags->comments;ci-->0;)_ogg_free(_tags->user_comments[ci]);
+ ncomments=_tags->comments;
+ if(_tags->user_comments!=NULL)ncomments++;
+ for(ci=ncomments;ci-->0;)_ogg_free(_tags->user_comments[ci]);
_ogg_free(_tags->user_comments);
_ogg_free(_tags->comment_lengths);
_ogg_free(_tags->vendor);
@@ -103,19 +106,27 @@
static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
char **user_comments;
int *comment_lengths;
+ int cur_ncomments;
+ char *binary_suffix_data;
+ int binary_suffix_len;
size_t size;
if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
+ cur_ncomments=_tags->comments;
+ comment_lengths=_tags->comment_lengths;
+ binary_suffix_len=comment_lengths==NULL?0:comment_lengths[cur_ncomments];
comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size);
if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
- comment_lengths[_ncomments]=0;
+ comment_lengths[_ncomments]=binary_suffix_len;
_tags->comment_lengths=comment_lengths;
size=sizeof(*_tags->user_comments)*(_ncomments+1);
if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT;
+ user_comments=_tags->user_comments;
+ binary_suffix_data=user_comments==NULL?NULL:user_comments[cur_ncomments];
user_comments=(char **)_ogg_realloc(_tags->user_comments,size);
if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
- user_comments[_ncomments]=NULL;
+ user_comments[_ncomments]=binary_suffix_data;
_tags->user_comments=user_comments;
return 0;
}
@@ -192,6 +203,13 @@
_data+=count;
len-=count;
}
+ if(len>0&&(_data[0]&1)){
+ if(len>(opus_uint32)INT_MAX)return OP_EFAULT;
+ _tags->user_comments[ncomments]=(char *)_ogg_malloc(len);
+ if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT;
+ memcpy(_tags->user_comments[ncomments],_data,len);
+ _tags->comment_lengths[ncomments]=(int)len;
+ }
return 0;
}
@@ -232,6 +250,16 @@
_dst->comment_lengths[ci]=len;
_dst->comments=ci+1;
}
+ if(_src->comment_lengths!=NULL){
+ int len;
+ len=_src->comment_lengths[ncomments];
+ if(len>0){
+ _dst->user_comments[ncomments]=(char *)_ogg_malloc(len);
+ if(OP_UNLIKELY(_dst->user_comments[ncomments]==NULL))return OP_EFAULT;
+ memcpy(_dst->user_comments[ncomments],_src->user_comments[ncomments],len);
+ _dst->comment_lengths[ncomments]=len;
+ }
+ }
return 0;
}
@@ -257,13 +285,12 @@
tag_len=strlen(_tag);
value_len=strlen(_value);
/*+2 for '=' and '\0'.*/
- _tags->comment_lengths[ncomments]=0;
- _tags->user_comments[ncomments]=comment=
- (char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
+ comment=(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
memcpy(comment,_tag,sizeof(*comment)*tag_len);
comment[tag_len]='=';
memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1));
+ _tags->user_comments[ncomments]=comment;
_tags->comment_lengths[ncomments]=tag_len+value_len+1;
_tags->comments=ncomments+1;
return 0;
@@ -270,21 +297,40 @@
}
int opus_tags_add_comment(OpusTags *_tags,const char *_comment){
- int comment_len;
- int ncomments;
- int ret;
+ char *comment;
+ int comment_len;
+ int ncomments;
+ int ret;
ncomments=_tags->comments;
ret=op_tags_ensure_capacity(_tags,ncomments+1);
if(OP_UNLIKELY(ret<0))return ret;
comment_len=(int)strlen(_comment);
- _tags->comment_lengths[ncomments]=0;
- _tags->user_comments[ncomments]=op_strdup_with_len(_comment,comment_len);
+ comment=op_strdup_with_len(_comment,comment_len);
if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT;
+ _tags->user_comments[ncomments]=comment;
_tags->comment_lengths[ncomments]=comment_len;
_tags->comments=ncomments+1;
return 0;
}
+int opus_tags_set_binary_suffix(OpusTags *_tags,
+ const unsigned char *_data,int _len){
+ unsigned char *binary_suffix_data;
+ int ncomments;
+ int ret;
+ if(_len<0||_len>0&&(_data==NULL||!(_data[0]&1)))return OP_EINVAL;
+ ncomments=_tags->comments;
+ ret=op_tags_ensure_capacity(_tags,ncomments);
+ if(OP_UNLIKELY(ret<0))return ret;
+ binary_suffix_data=
+ (unsigned char *)_ogg_realloc(_tags->user_comments[ncomments],_len);
+ if(OP_UNLIKELY(binary_suffix_data==NULL))return OP_EFAULT;
+ memcpy(binary_suffix_data,_data,_len);
+ _tags->user_comments[ncomments]=(char *)binary_suffix_data;
+ _tags->comment_lengths[ncomments]=_len;
+ return 0;
+}
+
int opus_tagcompare(const char *_tag_name,const char *_comment){
return opus_tagncompare(_tag_name,strlen(_tag_name),_comment);
}
@@ -330,6 +376,17 @@
if(!opus_tagncompare(_tag,tag_len,user_comments[ci]))found++;
}
return found;
+}
+
+const unsigned char *opus_tags_get_binary_suffix(const OpusTags *_tags,
+ int *_len){
+ int ncomments;
+ int len;
+ ncomments=_tags->comments;
+ len=_tags->comment_lengths==NULL?0:_tags->comment_lengths[ncomments];
+ *_len=len;
+ OP_ASSERT(len==0||_tags->user_comments!=NULL);
+ return len>0?(const unsigned char *)_tags->user_comments[ncomments]:NULL;
}
static int opus_tags_get_gain(const OpusTags *_tags,int *_gain_q8,