1 /* Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
3 /** \file opus_afh.c Audio format handler for ogg/opus files. */
11 #include "portable_io.h"
13 #include "opus_common.h"
14 #include "ogg_afh_common.h"
16 static const char * const opus_suffixes
[] = {"opus", NULL
};
18 #define OPUS_COMMENT_HEADER "OpusTags"
20 static bool copy_if_tag_type(const char *tag
, int taglen
, const char *type
,
23 char *q
= key_value_copy(tag
, taglen
, type
);
31 static int opus_get_comments(char *comments
, int length
,
34 char *p
= comments
, *end
= comments
+ length
;
38 /* min size of a opus header is 16 bytes */
40 return -E_OPUS_COMMENT
;
41 if (memcmp(p
, OPUS_COMMENT_HEADER
, strlen(OPUS_COMMENT_HEADER
)) != 0)
42 return -E_OPUS_COMMENT
;
47 return -E_OPUS_COMMENT
;
48 tags
->comment
= safe_strdup(p
, val
);
52 if (p
+ ntags
* 4 > end
)
53 return -E_OPUS_COMMENT
;
54 PARA_INFO_LOG("found %u tag(s)\n", ntags
);
55 for (i
= 0; i
< ntags
; i
++, p
+= val
) {
59 return -E_OPUS_COMMENT
;
63 return -E_OPUS_COMMENT
;
64 if (copy_if_tag_type(p
, val
, "author", &tags
->artist
))
66 if (copy_if_tag_type(p
, val
, "artist", &tags
->artist
))
68 if (copy_if_tag_type(p
, val
, "title", &tags
->title
))
70 if (copy_if_tag_type(p
, val
, "album", &tags
->album
))
72 if (copy_if_tag_type(p
, val
, "year", &tags
->year
))
74 if (copy_if_tag_type(p
, val
, "comment", &tags
->comment
))
76 tag
= safe_strdup(p
, val
);
77 PARA_NOTICE_LOG("unrecognized tag: %s\n", tag
);
84 * Ogg/Opus has two mandatory header packets:
86 * 1. ID header (identifies the stream as Opus). Dedicated "BOS" ogg page.
87 * 2. Comment header (metadata). May span multiple pages.
89 * See doc/draft-ietf-codec-oggopus.xml in the opus source tree for details.
91 static int opus_packet_callback(ogg_packet
*packet
, int packet_num
,
92 __a_unused
int serial
, struct afh_info
*afhi
,
96 struct opus_header
*oh
= private_data
;
98 if (packet_num
== 0) {
99 ret
= opus_parse_header((char *)packet
->packet
, packet
->bytes
, oh
);
102 afhi
->channels
= oh
->channels
;
103 afhi
->techinfo
= make_message(
104 "header version %d, input sample rate: %" PRIu32
"Hz",
105 oh
->version
, oh
->input_sample_rate
);
107 * The input sample rate is irrelevant for afhi->frequency as
108 * we always decode to 48kHz.
110 afhi
->frequency
= 48000;
113 if (packet_num
== 1) {
114 ret
= opus_get_comments((char *)packet
->packet
, packet
->bytes
,
118 return 0; /* header complete */
124 static int opus_get_file_info(char *map
, size_t numbytes
, __a_unused
int fd
,
125 struct afh_info
*afhi
)
128 struct opus_header oh
= {.version
= 0};
130 struct oac_callback_info opus_callback_info
= {
131 .packet_callback
= opus_packet_callback
,
134 ret
= oac_get_file_info(map
, numbytes
, afhi
, &opus_callback_info
);
137 ret
= (afhi
->chunk_table
[afhi
->chunks_total
] - afhi
->chunk_table
[0]) * 8; /* bits */
138 ms
= tv2ms(&afhi
->chunk_tv
) * afhi
->chunks_total
;
139 afhi
->bitrate
= ret
/ ms
;
143 static size_t opus_make_meta_packet(struct taginfo
*tags
, char **result
)
147 size_t comment_len
= strlen(tags
->comment
),
148 artist_len
= strlen(tags
->artist
),
149 title_len
= strlen(tags
->title
),
150 album_len
= strlen(tags
->album
),
151 year_len
= strlen(tags
->year
);
152 uint32_t comment_sz
= comment_len
,
153 artist_sz
= artist_len
+ strlen("artist="),
154 title_sz
= title_len
+ strlen("title="),
155 album_sz
= album_len
+ strlen("album="),
156 year_sz
= year_len
+ strlen("year=");
159 sz
= strlen(OPUS_COMMENT_HEADER
)
160 + 4 /* comment length (always present) */
162 + 4; /* number of tags */
180 PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz
);
181 /* terminating zero byte for the last sprintf() */
182 buf
= p
= para_malloc(sz
+ 1);
183 memcpy(p
, OPUS_COMMENT_HEADER
, strlen(OPUS_COMMENT_HEADER
));
184 p
+= strlen(OPUS_COMMENT_HEADER
);
185 write_u32(p
, comment_sz
);
187 strcpy(p
, tags
->comment
);
189 write_u32(p
, num_tags
);
192 write_u32(p
, artist_sz
);
194 sprintf(p
, "artist=%s", tags
->artist
);
198 write_u32(p
, title_sz
);
200 sprintf(p
, "title=%s", tags
->title
);
204 write_u32(p
, album_sz
);
206 sprintf(p
, "album=%s", tags
->album
);
210 write_u32(p
, year_sz
);
212 sprintf(p
, "year=%s", tags
->year
);
215 assert(p
== buf
+ sz
);
220 static int opus_rewrite_tags(const char *map
, size_t mapsize
,
221 struct taginfo
*tags
, int output_fd
,
222 __a_unused
const char *filename
)
228 meta_sz
= opus_make_meta_packet(tags
, &meta_packet
);
229 ret
= oac_rewrite_tags(map
, mapsize
, output_fd
, meta_packet
, meta_sz
);
235 * See doc/draft-ietf-codec-oggopus.xml in the opus source tree for details
236 * about the format of the comment header.
238 static int opus_get_header_callback(ogg_packet
*packet
, int packet_num
,
239 int serial
, __a_unused
struct afh_info
*afhi
, void *private_data
)
241 struct oac_custom_header
*h
= private_data
;
243 static unsigned char dummy_tags
[] = { /* a minimal comment header */
244 'O', 'p', 'u', 's', 'T', 'a', 'g', 's',
245 0x06, 0x00, 0x00, 0x00, /* vendor string length */
246 'd', 'u', 'm', 'm', 'y', '\0', /* vendor string */
247 0x00, 0x00, 0x00, 0x00, /* user comment list length */
249 ogg_packet replacement
;
251 if (packet_num
== 0) {
252 oac_custom_header_init(serial
, h
);
253 ret
= oac_custom_header_append(packet
, h
);
256 oac_custom_header_flush(h
);
259 assert(packet_num
== 1);
260 PARA_INFO_LOG("replacing metadata packet\n");
261 replacement
= *packet
;
262 replacement
.packet
= dummy_tags
;
263 replacement
.bytes
= sizeof(dummy_tags
);
264 ret
= oac_custom_header_append(&replacement
, h
);
267 oac_custom_header_flush(h
);
271 static void opus_get_header(void *map
, size_t mapsize
, char **buf
,
275 struct oac_custom_header
*h
= oac_custom_header_new();
276 struct oac_callback_info cb
= {
277 .packet_callback
= opus_get_header_callback
,
281 ret
= oac_get_file_info(map
, mapsize
, NULL
, &cb
);
282 *len
= oac_custom_header_get(buf
, h
);
284 PARA_ERROR_LOG("could not create custom header: %s\n",
285 para_strerror(-ret
));
290 PARA_INFO_LOG("created %zu byte ogg/opus header\n", *len
);
294 * The init function of the ogg/opus audio format handler.
296 * \param afh Pointer to the struct to initialize.
298 void opus_afh_init(struct audio_format_handler
*afh
)
300 afh
->get_file_info
= opus_get_file_info
,
301 afh
->get_header
= opus_get_header
;
302 afh
->suffixes
= opus_suffixes
;
303 afh
->rewrite_tags
= opus_rewrite_tags
;