Commit 1536b5a9 authored by Max Kellermann's avatar Max Kellermann

src/decoder/opus: new decoder plugin for the Opus codec

Using libopus and libogg.
parent bab756a5
......@@ -81,6 +81,9 @@ Alternative for MP3 support.
Ogg Vorbis - http://www.xiph.org/ogg/vorbis/
For Ogg Vorbis support. You will need libogg and libvorbis.
libopus - http://libopus.sourceforge.net/
Opus codec support
FLAC - http://flac.sourceforge.net/
For FLAC support. You will need version 1.1.0 or higher of libflac.
......
......@@ -538,6 +538,7 @@ libdecoder_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(WAVPACK_CFLAGS) \
$(MAD_CFLAGS) \
$(MPG123_CFLAGS) \
$(OPUS_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(MPCDEC_CFLAGS) \
$(FAAD_CFLAGS)
......@@ -555,6 +556,7 @@ DECODER_LIBS = \
$(WAVPACK_LIBS) \
$(MAD_LIBS) \
$(MPG123_LIBS) \
$(OPUS_LIBS) \
$(MP4FF_LIBS) \
$(FFMPEG_LIBS) \
$(MPCDEC_LIBS) \
......@@ -574,6 +576,19 @@ if HAVE_MPCDEC
libdecoder_plugins_a_SOURCES += src/decoder/mpcdec_decoder_plugin.c
endif
if HAVE_OPUS
libdecoder_plugins_a_SOURCES += \
src/decoder/OggUtil.cxx \
src/decoder/OggUtil.hxx \
src/decoder/OpusReader.hxx \
src/decoder/OpusHead.hxx \
src/decoder/OpusHead.cxx \
src/decoder/OpusTags.cxx \
src/decoder/OpusTags.hxx \
src/decoder/OpusDecoderPlugin.cxx \
src/decoder/OpusDecoderPlugin.h
endif
if HAVE_WAVPACK
libdecoder_plugins_a_SOURCES += src/decoder/wavpack_decoder_plugin.c
endif
......
ver 0.18 (2012/??/??)
* decoder:
- opus: new decoder plugin for the Opus codec
* improved decoder/output error reporting
ver 0.17.2 (2012/??/??)
......
......@@ -329,6 +329,11 @@ AC_ARG_ENABLE(openal,
[enable OpenAL support (default: disable)]),,
enable_openal=no)
AC_ARG_ENABLE(opus,
AS_HELP_STRING([--enable-opus],
[enable Opus codec support (default: auto)]),,
enable_opus=auto)
AC_ARG_ENABLE(oss,
AS_HELP_STRING([--disable-oss],
[disable OSS support (default: enable)]),,
......@@ -919,6 +924,14 @@ if test x$enable_modplug = xyes; then
fi
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
dnl -------------------------------- libopus ----------------------------------
MPD_AUTO_PKG(opus, OPUS, [opus ogg],
[opus decoder plugin], [libopus not found])
if test x$enable_opus = xyes; then
AC_DEFINE(HAVE_OPUS, 1, [Define to use libopus])
fi
AM_CONDITIONAL(HAVE_OPUS, test x$enable_opus = xyes)
dnl -------------------------------- libsndfile -------------------------------
dnl See above test, which may disable this.
MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile],
......@@ -1096,6 +1109,7 @@ if
test x$enable_mp4 = xno &&
test x$enable_mpc = xno &&
test x$enable_mpg123 = xno &&
test x$enable_opus = xno &&
test x$enable_sidplay = xno &&
test x$enable_tremor = xno &&
test x$enable_vorbis = xno &&
......@@ -1106,7 +1120,7 @@ if
fi
AM_CONDITIONAL(HAVE_XIPH,
test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes)
test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes || test x$enable_opus = xyes)
AM_CONDITIONAL(HAVE_FLAC_COMMON,
test x$enable_flac = xyes)
......@@ -1581,6 +1595,7 @@ results(mpg123, [MPG123])
results(mp4, [MP4])
results(mpc, [Musepack])
printf '\n\t'
results(opus, [OPUS])
results(tremor, [OggTremor])
results(vorbis, [OggVorbis])
results(audiofile, [WAVE])
......
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "OggUtil.hxx"
extern "C" {
#include "decoder_api.h"
}
bool
OggFeed(ogg_sync_state &oy, struct decoder *decoder,
struct input_stream *input_stream, size_t size)
{
char *buffer = ogg_sync_buffer(&oy, size);
if (buffer == nullptr)
return false;
size_t nbytes = decoder_read(decoder, input_stream,
buffer, size);
if (nbytes == 0)
return false;
ogg_sync_wrote(&oy, nbytes);
return true;
}
bool
OggExpectPage(ogg_sync_state &oy, ogg_page &page,
struct decoder *decoder, struct input_stream *input_stream)
{
while (true) {
int r = ogg_sync_pageout(&oy, &page);
if (r != 0)
return r > 0;
if (!OggFeed(oy, decoder, input_stream, 1024))
return false;
}
}
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_OGG_UTIL_HXX
#define MPD_OGG_UTIL_HXX
#include "check.h"
#include <ogg/ogg.h>
#include <stddef.h>
/**
* Feed data from the #input_stream into the #ogg_sync_state.
*
* @return false on error or end-of-file
*/
bool
OggFeed(ogg_sync_state &oy, struct decoder *decoder,
struct input_stream *input_stream, size_t size);
/**
* Feed into the #ogg_sync_state until a page gets available. Garbage
* data at the beginning is considered a fatal error.
*
* @return true if a page is available
*/
bool
OggExpectPage(ogg_sync_state &oy, ogg_page &page,
struct decoder *decoder, struct input_stream *input_stream);
#endif
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h" /* must be first for large file support */
#include "OpusDecoderPlugin.h"
#include "OpusHead.hxx"
#include "OpusTags.hxx"
#include "OggUtil.hxx"
extern "C" {
#include "ogg_codec.h"
#include "decoder_api.h"
}
#include "audio_check.h"
#include "tag_handler.h"
#include <opus.h>
#include <ogg/ogg.h>
#include <glib.h>
#include <stdio.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "opus"
static const opus_int32 opus_sample_rate = 48000;
gcc_pure
static bool
IsOpusHead(const ogg_packet &packet)
{
return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0;
}
gcc_pure
static bool
IsOpusTags(const ogg_packet &packet)
{
return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0;
}
static bool
mpd_opus_init(G_GNUC_UNUSED const struct config_param *param)
{
g_debug("%s", opus_get_version_string());
return true;
}
class MPDOpusDecoder {
struct decoder *decoder;
struct input_stream *input_stream;
ogg_stream_state os;
OpusDecoder *opus_decoder = nullptr;
opus_int16 *output_buffer = nullptr;
unsigned output_size = 0;
bool os_initialized = false;
bool found_opus = false;
int opus_serialno;
size_t frame_size;
public:
MPDOpusDecoder(struct decoder *_decoder,
struct input_stream *_input_stream)
:decoder(_decoder), input_stream(_input_stream) {}
~MPDOpusDecoder();
enum decoder_command HandlePage(ogg_page &page);
enum decoder_command HandlePacket(const ogg_packet &packet);
enum decoder_command HandleBOS(const ogg_packet &packet);
enum decoder_command HandleTags(const ogg_packet &packet);
enum decoder_command HandleAudio(const ogg_packet &packet);
};
MPDOpusDecoder::~MPDOpusDecoder()
{
g_free(output_buffer);
if (opus_decoder != nullptr)
opus_decoder_destroy(opus_decoder);
if (os_initialized)
ogg_stream_clear(&os);
}
enum decoder_command
MPDOpusDecoder::HandlePage(ogg_page &page)
{
const auto page_serialno = ogg_page_serialno(&page);
if (!os_initialized) {
os_initialized = true;
ogg_stream_init(&os, page_serialno);
} else if (page_serialno != os.serialno)
ogg_stream_reset_serialno(&os, page_serialno);
ogg_stream_pagein(&os, &page);
ogg_packet packet;
while (ogg_stream_packetout(&os, &packet) == 1) {
enum decoder_command cmd = HandlePacket(packet);
if (cmd != DECODE_COMMAND_NONE)
return cmd;
}
return DECODE_COMMAND_NONE;
}
enum decoder_command
MPDOpusDecoder::HandlePacket(const ogg_packet &packet)
{
if (packet.e_o_s)
return DECODE_COMMAND_STOP;
if (packet.b_o_s)
return HandleBOS(packet);
else if (!found_opus)
return DECODE_COMMAND_STOP;
if (IsOpusTags(packet))
return HandleTags(packet);
return HandleAudio(packet);
}
enum decoder_command
MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
{
assert(packet.b_o_s);
if (found_opus || !IsOpusHead(packet))
return DECODE_COMMAND_STOP;
unsigned channels;
if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
!audio_valid_channel_count(channels))
return DECODE_COMMAND_STOP;
assert(opus_decoder == nullptr);
assert(output_buffer == nullptr);
opus_serialno = os.serialno;
found_opus = true;
/* TODO: parse attributes from the OpusHead (sample rate,
channels, ...) */
int opus_error;
opus_decoder = opus_decoder_create(opus_sample_rate, channels,
&opus_error);
if (opus_decoder == nullptr) {
g_warning("libopus error: %s",
opus_strerror(opus_error));
return DECODE_COMMAND_STOP;
}
struct audio_format audio_format;
audio_format_init(&audio_format, opus_sample_rate,
SAMPLE_FORMAT_S16, channels);
decoder_initialized(decoder, &audio_format, false, -1);
frame_size = audio_format_frame_size(&audio_format);
/* allocate an output buffer for 16 bit PCM samples big enough
to hold a quarter second, larger than 120ms required by
libopus */
output_size = audio_format.sample_rate / 4;
output_buffer = (opus_int16 *)
g_malloc(sizeof(*output_buffer) * output_size *
audio_format.channels);
return decoder_get_command(decoder);
}
enum decoder_command
MPDOpusDecoder::HandleTags(const ogg_packet &packet)
{
struct tag *tag = tag_new();
enum decoder_command cmd;
if (ScanOpusTags(packet.packet, packet.bytes, &add_tag_handler, tag) &&
!tag_is_empty(tag))
cmd = decoder_tag(decoder, input_stream, tag);
else
cmd = decoder_get_command(decoder);
tag_free(tag);
return cmd;
}
enum decoder_command
MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
{
assert(opus_decoder != nullptr);
int nframes = opus_decode(opus_decoder,
(const unsigned char*)packet.packet,
packet.bytes,
output_buffer, output_size,
0);
if (nframes < 0) {
g_warning("%s", opus_strerror(nframes));
return DECODE_COMMAND_STOP;
}
if (nframes > 0) {
const size_t nbytes = nframes * frame_size;
enum decoder_command cmd =
decoder_data(decoder, input_stream,
output_buffer, nbytes,
0);
if (cmd != DECODE_COMMAND_NONE)
return cmd;
}
return DECODE_COMMAND_NONE;
}
static void
mpd_opus_stream_decode(struct decoder *decoder,
struct input_stream *input_stream)
{
if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_OPUS)
return;
/* rewind the stream, because ogg_codec_detect() has
moved it */
input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
MPDOpusDecoder d(decoder, input_stream);
ogg_sync_state oy;
ogg_sync_init(&oy);
while (true) {
if (!OggFeed(oy, decoder, input_stream, 1024))
break;
ogg_page page;
while (ogg_sync_pageout(&oy, &page) == 1) {
enum decoder_command cmd = d.HandlePage(page);
if (cmd != DECODE_COMMAND_NONE)
break;
}
}
ogg_sync_clear(&oy);
}
static bool
mpd_opus_scan_stream(struct input_stream *is,
const struct tag_handler *handler, void *handler_ctx)
{
ogg_sync_state oy;
ogg_sync_init(&oy);
ogg_page page;
if (!OggExpectPage(oy, page, nullptr, is)) {
ogg_sync_clear(&oy);
return false;
}
/* read at most two more pages */
unsigned remaining_pages = 2;
bool result = false;
ogg_stream_state os;
ogg_stream_init(&os, ogg_page_serialno(&page));
ogg_stream_pagein(&os, &page);
ogg_packet packet;
while (true) {
int r = ogg_stream_packetout(&os, &packet);
if (r < 0) {
result = false;
break;
}
if (r == 0) {
if (remaining_pages-- == 0)
break;
if (!OggExpectPage(oy, page, nullptr, is)) {
result = false;
break;
}
ogg_stream_pagein(&os, &page);
continue;
}
if (packet.b_o_s) {
if (!IsOpusHead(packet))
break;
unsigned channels;
if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
!audio_valid_channel_count(channels)) {
result = false;
break;
}
result = true;
} else if (!result)
break;
else if (IsOpusTags(packet)) {
if (!ScanOpusTags(packet.packet, packet.bytes,
handler, handler_ctx))
result = false;
break;
}
}
ogg_stream_clear(&os);
ogg_sync_clear(&oy);
return result;
}
static const char *const opus_suffixes[] = {
"opus",
"ogg",
"oga",
nullptr
};
static const char *const opus_mime_types[] = {
"audio/opus",
nullptr
};
const struct decoder_plugin opus_decoder_plugin = {
"opus",
mpd_opus_init,
nullptr,
mpd_opus_stream_decode,
nullptr,
nullptr,
mpd_opus_scan_stream,
nullptr,
opus_suffixes,
opus_mime_types,
};
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_DECODER_OPUS_H
#define MPD_DECODER_OPUS_H
extern const struct decoder_plugin opus_decoder_plugin;
#endif
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "OpusHead.hxx"
#include <stdint.h>
#include <string.h>
struct OpusHead {
char signature[8];
uint8_t version, channels;
uint16_t pre_skip;
uint32_t sample_rate;
uint16_t output_gain;
uint8_t channel_mapping;
};
bool
ScanOpusHeader(const void *data, size_t size, unsigned &channels_r)
{
const OpusHead *h = (const OpusHead *)data;
if (size < 19 || (h->version & 0xf0) != 0)
return false;
channels_r = h->channels;
return true;
}
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_OPUS_HEAD_HXX
#define MPD_OPUS_HEAD_HXX
#include "check.h"
#include <stddef.h>
bool
ScanOpusHeader(const void *data, size_t size, unsigned &channels_r);
#endif
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_OPUS_READER_HXX
#define MPD_OPUS_READER_HXX
#include "check.h"
#include <stdint.h>
#include <string.h>
class OpusReader {
const uint8_t *p, *const end;
public:
OpusReader(const void *_p, size_t size)
:p((const uint8_t *)_p), end(p + size) {}
bool Skip(size_t length) {
p += length;
return p <= end;
}
const void *Read(size_t length) {
const uint8_t *result = p;
return Skip(length)
? result
: nullptr;
}
bool Expect(const void *value, size_t length) {
const void *data = Read(length);
return data != nullptr && memcmp(value, data, length) == 0;
}
bool ReadByte(uint8_t &value_r) {
if (p >= end)
return false;
value_r = *p++;
return true;
}
bool ReadShort(uint16_t &value_r) {
const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
if (value == nullptr)
return false;
value_r = value[0] | (value[1] << 8);
return true;
}
bool ReadWord(uint32_t &value_r) {
const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
if (value == nullptr)
return false;
value_r = value[0] | (value[1] << 8)
| (value[2] << 16) | (value[3] << 24);
return true;
}
bool SkipString() {
uint32_t length;
return ReadWord(length) && Skip(length);
}
char *ReadString() {
uint32_t length;
if (!ReadWord(length))
return nullptr;
const char *src = (const char *)Read(length);
if (src == nullptr)
return nullptr;
return strndup(src, length);
}
};
#endif
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "OpusTags.hxx"
#include "OpusReader.hxx"
#include "XiphTags.h"
#include "tag_handler.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
static void
ScanOneOpusTag(const char *name, const char *value,
const struct tag_handler *handler, void *ctx)
{
tag_handler_invoke_pair(handler, ctx, name, value);
if (handler->tag != nullptr) {
enum tag_type t = tag_table_lookup_i(xiph_tags, name);
if (t != TAG_NUM_OF_ITEM_TYPES)
tag_handler_invoke_tag(handler, ctx, t, value);
}
}
bool
ScanOpusTags(const void *data, size_t size,
const struct tag_handler *handler, void *ctx)
{
OpusReader r(data, size);
if (!r.Expect("OpusTags", 8))
return false;
if (handler->pair == nullptr && handler->tag == nullptr)
return true;
if (!r.SkipString())
return false;
uint32_t n;
if (!r.ReadWord(n))
return false;
while (n-- > 0) {
char *p = r.ReadString();
if (p == nullptr)
return false;
char *eq = strchr(p, '=');
if (eq != nullptr && eq > p) {
*eq = 0;
ScanOneOpusTag(p, eq + 1, handler, ctx);
}
free(p);
}
return true;
}
/*
* Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_OPUS_TAGS_HXX
#define MPD_OPUS_TAGS_HXX
#include "check.h"
#include <stddef.h>
bool
ScanOpusTags(const void *data, size_t size,
const struct tag_handler *handler, void *ctx);
#endif
......@@ -41,5 +41,8 @@ ogg_codec_detect(struct decoder *decoder, struct input_stream *is)
memcmp(buf + 28, "fLaC", 4) == 0)
return OGG_CODEC_FLAC;
if (memcmp(buf + 28, "Opus", 4) == 0)
return OGG_CODEC_OPUS;
return OGG_CODEC_VORBIS;
}
......@@ -30,6 +30,7 @@ enum ogg_codec {
OGG_CODEC_UNKNOWN,
OGG_CODEC_VORBIS,
OGG_CODEC_FLAC,
OGG_CODEC_OPUS,
};
enum ogg_codec
......
......@@ -26,6 +26,7 @@
#include "decoder/pcm_decoder_plugin.h"
#include "decoder/dsdiff_decoder_plugin.h"
#include "decoder/dsf_decoder_plugin.h"
#include "decoder/OpusDecoderPlugin.h"
#include <glib.h>
......@@ -66,6 +67,9 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef HAVE_FLAC
&flac_decoder_plugin,
#endif
#ifdef HAVE_OPUS
&opus_decoder_plugin,
#endif
#ifdef ENABLE_SNDFILE
&sndfile_decoder_plugin,
#endif
......
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