ref: 7b669d31ad6b76c2a7e7f849afa2584c45e2ce69
parent: 8cdeac9e32be0ea52fddccb1787dea446d552e4c
author: Jean-Marc Valin <jmvalin@jmvalin.ca>
date: Wed May 10 12:48:31 EDT 2017
Added comments object
--- a/examples/opusenc_example.c
+++ b/examples/opusenc_example.c
@@ -6,6 +6,7 @@
int main(int argc, char **argv) {
FILE *fin;
OggOpusEnc *enc;
+ OggOpusComments *comments;
int error;
if (argc != 3) {
fprintf(stderr, "usage: %s <raw pcm input> <Ogg Opus output>\n", argv[0]);
@@ -16,14 +17,15 @@
fprintf(stderr, "cannot open input file: %s\n", argv[1]);
return 1;
}
- enc = ope_create_file(argv[2], 48000, 2, 0, &error);
+ comments = ope_comments_create();
+ ope_comments_add(comments, "ARTIST", "Someone");
+ ope_comments_add(comments, "TITLE", "Some track");
+ enc = ope_create_file(argv[2], comments, 48000, 2, 0, &error);
if (!enc) {
fprintf(stderr, "cannout open output file: %s\n", argv[2]);
fclose(fin);
return 1;
}
- ope_add_comment(enc, "ARTIST", "Someone");
- ope_add_comment(enc, "TITLE", "Some track");
while (1) {
short buf[2*READ_SIZE];
int ret = fread(buf, 2*sizeof(short), READ_SIZE, fin);
@@ -33,6 +35,7 @@
}
ope_drain(enc);
ope_destroy(enc);
+ ope_comments_destroy(comments);
fclose(fin);
return 0;
}
--- a/include/opusenc.h
+++ b/include/opusenc.h
@@ -99,18 +99,37 @@
ope_close_func close;
} OpusEncCallbacks;
+/** Opaque comments struct. */
+typedef struct OggOpusComments OggOpusComments;
+
/** Opaque encoder struct. */
typedef struct OggOpusEnc OggOpusEnc;
+/** Create a new comments object. */
+OPE_EXPORT OggOpusComments *ope_comments_create();
+
+/** Create a deep copy of a comments object. */
+OPE_EXPORT OggOpusComments *ope_comments_copy(OggOpusComments *comments);
+
+/** Destroys a comments object. */
+OPE_EXPORT void ope_comments_destroy(OggOpusComments *comments);
+
+/** Add a comment. */
+OPE_EXPORT int ope_comments_add(OggOpusComments *comments, const char *tag, const char *val);
+
+/** Add a picture. */
+OPE_EXPORT int ope_comments_add_picture(OggOpusComments *comments, const char *spec);
+
+
/** Create a new OggOpus file. */
-OPE_EXPORT OggOpusEnc *ope_create_file(const char *path, int rate, int channels, int family, int *error);
+OPE_EXPORT OggOpusEnc *ope_create_file(const char *path, const OggOpusComments *comments, int rate, int channels, int family, int *error);
/** Create a new OggOpus file (callback-based). */
OPE_EXPORT OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_data,
- int rate, int channels, int family, int *error);
+ const OggOpusComments *comments, int rate, int channels, int family, int *error);
/** Create a new OggOpus stream, pulling one page at a time. */
-OPE_EXPORT OggOpusEnc *ope_create_pull(int rate, int channels, int family, int *error);
+OPE_EXPORT OggOpusEnc *ope_create_pull(const OggOpusComments *comments, int rate, int channels, int family, int *error);
/** Add/encode any number of float samples to the file. */
OPE_EXPORT int ope_write_float(OggOpusEnc *enc, const float *pcm, int samples_per_channel);
@@ -128,22 +147,13 @@
OPE_EXPORT void ope_destroy(OggOpusEnc *enc);
/** Ends the stream and create a new stream within the same file. */
-OPE_EXPORT int ope_chain_current(OggOpusEnc *enc);
+OPE_EXPORT int ope_chain_current(OggOpusEnc *enc, const OggOpusComments *comments);
/** Ends the stream and create a new file. */
-OPE_EXPORT int ope_continue_new_file(OggOpusEnc *enc, const char *path);
+OPE_EXPORT int ope_continue_new_file(OggOpusEnc *enc, const char *path, const OggOpusComments *comments);
/** Ends the stream and create a new file (callback-based). */
-OPE_EXPORT int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data);
-
-/** Add a comment to the file (can only be called before encoding samples). */
-OPE_EXPORT int ope_add_comment(OggOpusEnc *enc, const char *tag, const char *val);
-
-/** Add a picture to the file (can only be called before encoding samples). */
-OPE_EXPORT int ope_add_picture(OggOpusEnc *enc, const char *spec);
-
-/** Sets the Opus comment vendor string (optional, defaults to library info). */
-OPE_EXPORT int ope_set_vendor_string(OggOpusEnc *enc, const char *vendor);
+OPE_EXPORT int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data, const OggOpusComments *comments);
/** Write out the header now rather than wait for audio to begin. */
OPE_EXPORT int ope_flush_header(OggOpusEnc *enc);
--- a/src/opusenc.c
+++ b/src/opusenc.c
@@ -71,6 +71,73 @@
FILE *file;
};
+struct OggOpusComments {
+ char *comment;
+ int comment_length;
+ int seen_file_icons;
+};
+
+/* Create a new comments object. The vendor string is optional. */
+OggOpusComments *ope_comments_create() {
+ OggOpusComments *c;
+ const char *libopus_str;
+ char vendor_str[1024];
+ c = malloc(sizeof(*c));
+ if (c == NULL) return NULL;
+ libopus_str = opus_get_version_string();
+ snprintf(vendor_str, sizeof(vendor_str), "%s, %s version %s", libopus_str, PACKAGE_NAME, PACKAGE_VERSION);
+ comment_init(&c->comment, &c->comment_length, vendor_str);
+ if (c->comment == NULL) {
+ free(c);
+ return NULL;
+ } else {
+ return c;
+ }
+}
+
+/* Create a deep copy of a comments object. */
+OggOpusComments *ope_comments_copy(OggOpusComments *comments) {
+ OggOpusComments *c;
+ c = malloc(sizeof(*c));
+ if (c == NULL) return NULL;
+ memcpy(c, comments, sizeof(*c));
+ c->comment = malloc(comments->comment_length);
+ if (c->comment == NULL) {
+ free(c);
+ return NULL;
+ } else {
+ memcpy(c->comment, comments->comment, comments->comment_length);
+ return c;
+ }
+}
+
+/* Destroys a comments object. */
+void ope_comments_destroy(OggOpusComments *comments){
+ free(comments->comment);
+ free(comments);
+}
+
+/* Add a comment. */
+int ope_comments_add(OggOpusComments *comments, const char *tag, const char *val) {
+ if (comment_add(&comments->comment, &comments->comment_length, tag, val)) return OPE_ALLOC_FAIL;
+ return OPE_OK;
+}
+
+int ope_comments_add_picture(OggOpusComments *comments, const char *spec) {
+ const char *error_message;
+ char *picture_data;
+ picture_data = parse_picture_specification(spec, &error_message, &comments->seen_file_icons);
+ if(picture_data==NULL){
+ /* FIXME: return proper errors rather than printing a message. */
+ fprintf(stderr,"Error parsing picture option: %s\n",error_message);
+ return OPE_BAD_ARG;
+ }
+ comment_add(&comments->comment, &comments->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
+ free(picture_data);
+ return OPE_OK;
+}
+
+
typedef struct EncStream EncStream;
struct EncStream {
@@ -183,11 +250,11 @@
};
/* Create a new OggOpus file. */
-OggOpusEnc *ope_create_file(const char *path, int rate, int channels, int family, int *error) {
+OggOpusEnc *ope_create_file(const char *path, const OggOpusComments *comments, int rate, int channels, int family, int *error) {
OggOpusEnc *enc;
struct StdioObject *obj;
obj = malloc(sizeof(*obj));
- enc = ope_create_callbacks(&stdio_callbacks, obj, rate, channels, family, error);
+ enc = ope_create_callbacks(&stdio_callbacks, obj, comments, rate, channels, family, error);
if (enc == NULL || (error && *error)) {
return NULL;
}
@@ -200,7 +267,7 @@
return enc;
}
-EncStream *stream_create() {
+EncStream *stream_create(const OggOpusComments *comments) {
EncStream *stream;
stream = malloc(sizeof(*stream));
if (!stream) return NULL;
@@ -207,18 +274,14 @@
stream->next = NULL;
stream->close_at_end = 1;
stream->serialno_is_set = 0;
- stream->seen_file_icons = 0;
stream->stream_is_init = 0;
stream->header_is_frozen = 0;
stream->granule_offset = 0;
- stream->comment = NULL;
- comment_init(&stream->comment, &stream->comment_length, opus_get_version_string());
- if (!stream->comment) goto fail;
- {
- char encoder_string[1024];
- snprintf(encoder_string, sizeof(encoder_string), "%s version %s", PACKAGE_NAME, PACKAGE_VERSION);
- comment_add(&stream->comment, &stream->comment_length, "ENCODER", encoder_string);
- }
+ stream->comment = malloc(comments->comment_length);
+ if (stream->comment == NULL) goto fail;
+ memcpy(stream->comment, comments->comment, comments->comment_length);
+ stream->comment_length = comments->comment_length;
+ stream->seen_file_icons = comments->seen_file_icons;
return stream;
fail:
if (stream->comment) free(stream->comment);
@@ -236,7 +299,7 @@
/* Create a new OggOpus file (callback-based). */
OggOpusEnc *ope_create_callbacks(const OpusEncCallbacks *callbacks, void *user_data,
- int rate, int channels, int family, int *error) {
+ const OggOpusComments *comments, int rate, int channels, int family, int *error) {
OpusMSEncoder *st=NULL;
OggOpusEnc *enc=NULL;
int ret;
@@ -255,7 +318,7 @@
if ( (enc = malloc(sizeof(*enc))) == NULL) goto fail;
enc->streams = NULL;
- if ( (enc->streams = stream_create()) == NULL) goto fail;
+ if ( (enc->streams = stream_create(comments)) == NULL) goto fail;
enc->streams->next = NULL;
enc->last_stream = enc->streams;
#ifdef USE_OGGP
@@ -322,8 +385,8 @@
}
/* Create a new OggOpus stream, pulling one page at a time. */
-OPE_EXPORT OggOpusEnc *ope_create_pull(int rate, int channels, int family, int *error) {
- OggOpusEnc *enc = ope_create_callbacks(NULL, NULL, rate, channels, family, error);
+OPE_EXPORT OggOpusEnc *ope_create_pull(const OggOpusComments *comments, int rate, int channels, int family, int *error) {
+ OggOpusEnc *enc = ope_create_callbacks(NULL, NULL, comments, rate, channels, family, error);
enc->pull_api = 1;
return enc;
}
@@ -660,13 +723,13 @@
}
/* Ends the stream and create a new stream within the same file. */
-int ope_chain_current(OggOpusEnc *enc) {
+int ope_chain_current(OggOpusEnc *enc, const OggOpusComments *comments) {
enc->last_stream->close_at_end = 0;
- return ope_continue_new_callbacks(enc, enc->last_stream->user_data);
+ return ope_continue_new_callbacks(enc, enc->last_stream->user_data, comments);
}
/* Ends the stream and create a new file. */
-int ope_continue_new_file(OggOpusEnc *enc, const char *path) {
+int ope_continue_new_file(OggOpusEnc *enc, const char *path, const OggOpusComments *comments) {
int ret;
struct StdioObject *obj;
if (!(obj = malloc(sizeof(*obj)))) return OPE_ALLOC_FAIL;
@@ -676,7 +739,7 @@
/* By trying to open the file first, we can recover if we can't open it. */
return OPE_CANNOT_OPEN;
}
- ret = ope_continue_new_callbacks(enc, obj);
+ ret = ope_continue_new_callbacks(enc, obj, comments);
if (ret == OPE_OK) return ret;
fclose(obj->file);
free(obj);
@@ -684,51 +747,16 @@
}
/* Ends the stream and create a new file (callback-based). */
-int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data) {
+int ope_continue_new_callbacks(OggOpusEnc *enc, void *user_data, const OggOpusComments *comments) {
if (enc->unrecoverable) return OPE_UNRECOVERABLE;
EncStream *new_stream;
assert(enc->streams);
assert(enc->last_stream);
- new_stream = stream_create();
+ new_stream = stream_create(comments);
if (!new_stream) return OPE_ALLOC_FAIL;
new_stream->user_data = user_data;
enc->last_stream->next = new_stream;
enc->last_stream = new_stream;
- return OPE_OK;
-}
-
-/* Add a comment to the file (can only be called before encoding samples). */
-int ope_add_comment(OggOpusEnc *enc, const char *tag, const char *val) {
- if (enc->unrecoverable) return OPE_UNRECOVERABLE;
- if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
- if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
- if (comment_add(&enc->last_stream->comment, &enc->last_stream->comment_length, tag, val)) return OPE_ALLOC_FAIL;
- return OPE_OK;
-}
-
-int ope_add_picture(OggOpusEnc *enc, const char *spec) {
- const char *error_message;
- char *picture_data;
- if (enc->unrecoverable) return OPE_UNRECOVERABLE;
- if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
- if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
- picture_data = parse_picture_specification(spec, &error_message, &enc->last_stream->seen_file_icons);
- if(picture_data==NULL){
- /* FIXME: return proper errors rather than printing a message. */
- fprintf(stderr,"Error parsing picture option: %s\n",error_message);
- return OPE_BAD_ARG;
- }
- comment_add(&enc->last_stream->comment, &enc->last_stream->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
- free(picture_data);
- return OPE_OK;
-}
-
-/* Sets the Opus comment vendor string (optional, defaults to library info). */
-int ope_set_vendor_string(OggOpusEnc *enc, const char *vendor) {
- if (enc->unrecoverable) return OPE_UNRECOVERABLE;
- if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
- if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
- if (comment_replace_vendor_string(&enc->last_stream->comment, &enc->last_stream->comment_length, vendor)) return OPE_ALLOC_FAIL;
return OPE_OK;
}