Commit 41e89f5d authored by ogg.k.ogg.k's avatar ogg.k.ogg.k Committed by Vincent Penquerc'h

Opus support

parent 8698926d
......@@ -11,7 +11,7 @@ oggz-chop \(em Extract the part of an Ogg file between given start and/or end ti
.PP
\fBoggz-chop\fR chops a section of an Ogg file.
It correctly interprets the granulepos timestamps of
Ogg CELT, CMML, Dirac, FLAC, Kate, PCM, Speex, Theora and Vorbis
Ogg CELT, CMML, Dirac, FLAC, Kate, Opus, PCM, Speex, Theora and Vorbis
bitstreams.
Run \fBoggz-known-codecs\fP\fB(1)\fP for a full list
of codecs known by the installed version of oggz.
......
......@@ -13,7 +13,7 @@ presentation time.
\fBoggz-merge\fR merges Ogg files together, interleaving
pages in order of presentation time.
It correctly interprets the granulepos timestamps of
Ogg CELT, CMML, Dirac, FLAC, Kate, PCM, Speex, Theora and Vorbis
Ogg CELT, CMML, Dirac, FLAC, Kate, Opus, PCM, Speex, Theora and Vorbis
bitstreams.
Run \fBoggz-known-codecs\fP\fB(1)\fP for a full list
of codecs known by the installed version of oggz.
......
......@@ -12,7 +12,7 @@ oggz-sort \(em Sort the pages of an Ogg file in order of presentation time.
\fBoggz-sort\fR sorts an Ogg file, interleaving
pages in order of presentation time.
It correctly interprets the granulepos timestamps of
Ogg CELT, CMML, Dirac, FLAC, Kate, PCM, Speex, Theora and Vorbis
Ogg CELT, CMML, Dirac, FLAC, Kate, Opus, PCM, Speex, Theora and Vorbis
bitstreams.
Run \fBoggz-known-codecs\fP\fB(1)\fP for a full list
of codecs known by the installed version of oggz.
......
......@@ -10,7 +10,7 @@ oggz \(em inspect and manipulate Ogg multimedia files
\fBoggz\fR is a suite of tools for manipulating
Ogg multimedia files.
It supports multiplexed files conformant with RFC3533. Oggz can parse headers for
CELT, CMML, Dirac, FLAC, Kate, PCM, Speex, Theora and Vorbis, and can read and write
CELT, CMML, Dirac, FLAC, Kate, Opus, PCM, Speex, Theora and Vorbis, and can read and write
Ogg Skeleton logical bitstreams.
.SH "Commands"
......
......@@ -117,6 +117,7 @@ typedef enum OggzStreamContent {
OGGZ_CONTENT_CELT,
OGGZ_CONTENT_KATE,
OGGZ_CONTENT_DIRAC,
OGGZ_CONTENT_OPUS,
OGGZ_CONTENT_UNKNOWN
} OggzStreamContent;
......
......@@ -395,6 +395,34 @@ auto_dirac (OGGZ * oggz, long serialno, unsigned char * data, long length, void
return 1;
}
static int
auto_opus (OGGZ * oggz, long serialno, unsigned char * data, long length, void * user_data)
{
unsigned char * header = data;
unsigned char nchannels;
if (length < 19) return 0;
nchannels = data[9];
if (nchannels < 1) {
#ifdef DEBUG
printf("Opus header with 0 channels, invalid");
#endif
return 0;
}
#ifdef DEBUG
printf ("Got opus, %d channels\n", nchannels);
#endif
oggz_set_granulerate (oggz, serialno, 48000, OGGZ_AUTO_MULT);
oggz_set_granuleshift (oggz, serialno, 0);
oggz_stream_set_numheaders (oggz, serialno, 2);
return 1;
}
static int
auto_fisbone (OGGZ * oggz, long serialno, unsigned char * data, long length, void * user_data)
{
......@@ -556,6 +584,92 @@ auto_calc_celt (ogg_int64_t now, oggz_stream_t *stream, ogg_packet *op) {
return 0;
}
/*
* The first two Opus packets are header and comment packets (granulepos = 0)
*/
static ogg_int64_t
opus_packet_duration (ogg_packet *op)
{
static const unsigned int durations[32] = {
480, 960, 1920, 2880, /* Silk NB */
480, 960, 1920, 2880, /* Silk MB */
480, 960, 1920, 2880, /* Silk WB */
480, 960, /* Hybrid SWB */
480, 960, /* Hybrid FB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
};
unsigned char toc, code, nframes;
int frame_duration, duration;
if (op->bytes < 1)
return 0;
toc = op->packet[0];
code = toc & 3;
frame_duration = durations[toc >> 3];
if (code == 3 && op->bytes < 2)
return 0;
switch (code) {
case 0: nframes = 1; break;
case 1: case 2: nframes = 2; break;
case 3: nframes = op->packet[1] & 63; break;
}
duration = frame_duration * nframes;
if (duration > 5760)
return 0;
return duration;
}
typedef struct {
int headers_encountered;
int encountered_first_data_packet;
} auto_calc_opus_info_t;
static ogg_int64_t
auto_calc_opus(ogg_int64_t now, oggz_stream_t *stream, ogg_packet *op) {
auto_calc_opus_info_t *info
= (auto_calc_opus_info_t *)stream->calculate_data;
if (stream->calculate_data == NULL) {
stream->calculate_data = oggz_malloc(sizeof(auto_calc_opus_info_t));
if (stream->calculate_data == NULL) return -1;
info = stream->calculate_data;
info->encountered_first_data_packet = 0;
info->headers_encountered = 1;
return 0;
}
if (info->headers_encountered < 2) {
info->headers_encountered += 1;
} else {
info->encountered_first_data_packet = 1;
}
if (now > -1) {
return now;
}
if (info->encountered_first_data_packet) {
if (stream->last_granulepos > 0) {
return stream->last_granulepos + opus_packet_duration(op);
}
return -1;
}
return 0;
}
/*
* Header packets are marked by a set MSB in the first byte. Inter packets
* are marked by a set 2MSB in the first byte. Intra packets (keyframes)
......@@ -1105,6 +1219,7 @@ const oggz_auto_contenttype_t oggz_auto_codec_ident[] = {
{"CELT ", 8, "CELT", auto_celt, auto_calc_celt, NULL},
{"\200kate\0\0\0", 8, "Kate", auto_kate, NULL, NULL},
{"BBCD\0", 5, "Dirac", auto_dirac, NULL, NULL},
{"OpusHead", 8, "Opus", auto_opus, auto_calc_opus, NULL},
{"", 0, "Unknown", NULL, NULL, NULL}
};
......@@ -1229,6 +1344,11 @@ oggz_auto_read_comments (OGGZ * oggz, oggz_stream_t * stream, long serialno,
offset = 4;
}
break;
case OGGZ_CONTENT_OPUS:
if (op->bytes > 8 && memcmp (op->packet, "OpusHead", 8) == 0) {
offset = 8;
}
break;
default:
break;
}
......
......@@ -830,6 +830,8 @@ oggz_comment_generate(OGGZ * oggz, long serialno,
{0x04, 0x00, 0x00, 0x00};
const unsigned char preamble_kate[9] =
{0x81, 0x6b, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00};
const unsigned char preamble_opus[8] =
{'O', 'p', 'u', 's', 'T', 'a', 'g', 's'};
switch(packet_type) {
......@@ -849,6 +851,10 @@ oggz_comment_generate(OGGZ * oggz, long serialno,
preamble_length = sizeof preamble_kate;
preamble = preamble_kate;
break;
case OGGZ_CONTENT_OPUS:
preamble_length = sizeof preamble_opus;
preamble = preamble_opus;
break;
case OGGZ_CONTENT_PCM:
case OGGZ_CONTENT_SPEEX:
preamble_length = 0;
......@@ -909,7 +915,7 @@ oggz_comment_generate(OGGZ * oggz, long serialno,
return c_packet;
}
/* In Flac, OggPCM, Speex, Theora Vorbis, and Kate the comment packet will
/* In Flac, OggPCM, Speex, Theora, Vorbis, Kate, and Opus the comment packet will
be second in the stream, i.e. packetno=1, and it will have granulepos=0 */
ogg_packet *
oggz_comments_generate(OGGZ * oggz, long serialno,
......
......@@ -17,6 +17,7 @@ const char * const mime_type_names[] = {
"audio/celt",
"application/x-kate",
"video/dirac",
"audio/x-opus",
NULL /* UNKNOWN */
};
......
......@@ -47,7 +47,7 @@ usage (char * progname)
printf ("oggz is a commandline tool for manipulating Ogg files. It supports\n"
"multiplexed files conformant with RFC3533. Oggz can parse headers for\n"
"CELT, CMML, FLAC, Kate, PCM, Speex, Theora and Vorbis, and can read and write\n"
"CELT, CMML, FLAC, Kate, Opus, PCM, Speex, Theora and Vorbis, and can read and write\n"
"Ogg Skeleton logical bitstreams.\n");
printf ("\nCommands:\n");
......
......@@ -277,6 +277,22 @@ ot_kate_info (unsigned char * data, long len)
return buf;
}
static char *
ot_opus_info (unsigned char * data, long len)
{
char * buf;
if (len < 19) return NULL;
buf = malloc (40);
snprintf (buf, 40,
"\tAudio-Channels: %d\n",
data[9]);
return buf;
}
static char *
ot_dirac_info (unsigned char * data, long len)
{
......@@ -354,6 +370,7 @@ static const OTCodecInfoFunc codec_ident[] = {
ot_celt_info, /* CELT */
ot_kate_info, /* KATE */
ot_dirac_info, /* BBCD */
ot_opus_info, /* OPUS */
NULL /* UNKNOWN */
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment