decoder/opus: move code to class OggVisitor
This commit is contained in:
@@ -585,6 +585,7 @@ if HAVE_XIPH
|
|||||||
noinst_LIBRARIES += libxiph.a
|
noinst_LIBRARIES += libxiph.a
|
||||||
|
|
||||||
libxiph_a_SOURCES = \
|
libxiph_a_SOURCES = \
|
||||||
|
src/lib/xiph/OggVisitor.cxx src/lib/xiph/OggVisitor.hxx \
|
||||||
src/lib/xiph/VorbisComment.hxx \
|
src/lib/xiph/VorbisComment.hxx \
|
||||||
src/lib/xiph/VorbisComments.cxx src/lib/xiph/VorbisComments.hxx \
|
src/lib/xiph/VorbisComments.cxx src/lib/xiph/VorbisComments.hxx \
|
||||||
src/lib/xiph/XiphTags.cxx src/lib/xiph/XiphTags.hxx
|
src/lib/xiph/XiphTags.cxx src/lib/xiph/XiphTags.hxx
|
||||||
|
@@ -23,8 +23,7 @@
|
|||||||
#include "OpusHead.hxx"
|
#include "OpusHead.hxx"
|
||||||
#include "OpusTags.hxx"
|
#include "OpusTags.hxx"
|
||||||
#include "OggFind.hxx"
|
#include "OggFind.hxx"
|
||||||
#include "lib/xiph/OggStreamState.hxx"
|
#include "lib/xiph/OggVisitor.hxx"
|
||||||
#include "lib/xiph/OggSyncState.hxx"
|
|
||||||
#include "../DecoderAPI.hxx"
|
#include "../DecoderAPI.hxx"
|
||||||
#include "decoder/Reader.hxx"
|
#include "decoder/Reader.hxx"
|
||||||
#include "input/Reader.hxx"
|
#include "input/Reader.hxx"
|
||||||
@@ -73,13 +72,10 @@ mpd_opus_init(gcc_unused const ConfigBlock &block)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MPDOpusDecoder {
|
class MPDOpusDecoder final : public OggVisitor {
|
||||||
Decoder &decoder;
|
Decoder &decoder;
|
||||||
InputStream &input_stream;
|
InputStream &input_stream;
|
||||||
|
|
||||||
OggSyncState oy;
|
|
||||||
OggStreamState os;
|
|
||||||
|
|
||||||
OpusDecoder *opus_decoder = nullptr;
|
OpusDecoder *opus_decoder = nullptr;
|
||||||
opus_int16 *output_buffer = nullptr;
|
opus_int16 *output_buffer = nullptr;
|
||||||
|
|
||||||
@@ -97,21 +93,12 @@ class MPDOpusDecoder {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
MPDOpusDecoder(DecoderReader &reader)
|
MPDOpusDecoder(DecoderReader &reader)
|
||||||
:decoder(reader.GetDecoder()),
|
:OggVisitor(reader),
|
||||||
input_stream(reader.GetInputStream()),
|
decoder(reader.GetDecoder()),
|
||||||
oy(reader),
|
input_stream(reader.GetInputStream()) {}
|
||||||
os(0) {}
|
|
||||||
|
|
||||||
~MPDOpusDecoder();
|
~MPDOpusDecoder();
|
||||||
|
|
||||||
/**
|
|
||||||
* Has the OggStreamState been initialized with the first
|
|
||||||
* serial?
|
|
||||||
*/
|
|
||||||
bool HasSerial() const {
|
|
||||||
return os.GetSerialNo() != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has decoder_initialized() been called yet?
|
* Has decoder_initialized() been called yet?
|
||||||
*/
|
*/
|
||||||
@@ -119,18 +106,19 @@ public:
|
|||||||
return previous_channels != 0;
|
return previous_channels != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadNextPage();
|
|
||||||
|
|
||||||
DecoderCommand HandlePackets();
|
DecoderCommand HandlePackets();
|
||||||
|
|
||||||
bool Seek(uint64_t where_frame);
|
bool Seek(uint64_t where_frame);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DecoderCommand HandlePacket(const ogg_packet &packet);
|
void HandleTags(const ogg_packet &packet);
|
||||||
void HandleBOS(const ogg_packet &packet);
|
void HandleAudio(const ogg_packet &packet);
|
||||||
void HandleEOS();
|
|
||||||
DecoderCommand HandleTags(const ogg_packet &packet);
|
protected:
|
||||||
DecoderCommand HandleAudio(const ogg_packet &packet);
|
/* virtual methods from class OggVisitor */
|
||||||
|
void OnOggBeginning(const ogg_packet &packet) override;
|
||||||
|
void OnOggPacket(const ogg_packet &packet) override;
|
||||||
|
void OnOggEnd() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
MPDOpusDecoder::~MPDOpusDecoder()
|
MPDOpusDecoder::~MPDOpusDecoder()
|
||||||
@@ -141,52 +129,13 @@ MPDOpusDecoder::~MPDOpusDecoder()
|
|||||||
opus_decoder_destroy(opus_decoder);
|
opus_decoder_destroy(opus_decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
void
|
||||||
MPDOpusDecoder::ReadNextPage()
|
MPDOpusDecoder::OnOggPacket(const ogg_packet &packet)
|
||||||
{
|
{
|
||||||
ogg_page page;
|
|
||||||
if (!oy.ExpectPage(page))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto page_serialno = ogg_page_serialno(&page);
|
|
||||||
if (page_serialno != os.GetSerialNo())
|
|
||||||
os.Reinitialize(page_serialno);
|
|
||||||
|
|
||||||
os.PageIn(page);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline DecoderCommand
|
|
||||||
MPDOpusDecoder::HandlePackets()
|
|
||||||
{
|
|
||||||
ogg_packet packet;
|
|
||||||
while (os.PacketOut(packet) == 1) {
|
|
||||||
auto cmd = HandlePacket(packet);
|
|
||||||
if (cmd != DecoderCommand::NONE)
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DecoderCommand::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline DecoderCommand
|
|
||||||
MPDOpusDecoder::HandlePacket(const ogg_packet &packet)
|
|
||||||
{
|
|
||||||
if (packet.e_o_s) {
|
|
||||||
HandleEOS();
|
|
||||||
return decoder_get_command(decoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet.b_o_s) {
|
|
||||||
HandleBOS(packet);
|
|
||||||
return decoder_get_command(decoder);
|
|
||||||
} else if (opus_decoder == nullptr)
|
|
||||||
throw std::runtime_error("BOS packet expected");
|
|
||||||
|
|
||||||
if (IsOpusTags(packet))
|
if (IsOpusTags(packet))
|
||||||
return HandleTags(packet);
|
HandleTags(packet);
|
||||||
|
else
|
||||||
return HandleAudio(packet);
|
HandleAudio(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,8 +189,8 @@ LoadEOSGranulePos(InputStream &is, Decoder &decoder, int serialno)
|
|||||||
return packet.granulepos;
|
return packet.granulepos;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
void
|
||||||
MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
|
MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet)
|
||||||
{
|
{
|
||||||
assert(packet.b_o_s);
|
assert(packet.b_o_s);
|
||||||
|
|
||||||
@@ -260,7 +209,7 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
|
|||||||
throw FormatRuntimeError("Next stream has different channels (%u -> %u)",
|
throw FormatRuntimeError("Next stream has different channels (%u -> %u)",
|
||||||
previous_channels, channels);
|
previous_channels, channels);
|
||||||
|
|
||||||
const auto opus_serialno = os.GetSerialNo();
|
const auto opus_serialno = GetSerialNo();
|
||||||
|
|
||||||
/* TODO: parse attributes from the OpusHead (sample rate,
|
/* TODO: parse attributes from the OpusHead (sample rate,
|
||||||
channels, ...) */
|
channels, ...) */
|
||||||
@@ -295,10 +244,14 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
|
|||||||
|
|
||||||
output_buffer = new opus_int16[opus_output_buffer_frames
|
output_buffer = new opus_int16[opus_output_buffer_frames
|
||||||
* audio_format.channels];
|
* audio_format.channels];
|
||||||
|
|
||||||
|
auto cmd = decoder_get_command(decoder);
|
||||||
|
if (cmd != DecoderCommand::NONE)
|
||||||
|
throw cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
void
|
||||||
MPDOpusDecoder::HandleEOS()
|
MPDOpusDecoder::OnOggEnd()
|
||||||
{
|
{
|
||||||
if (eos_granulepos < 0 && IsInitialized()) {
|
if (eos_granulepos < 0 && IsInitialized()) {
|
||||||
/* allow chaining of (unseekable) streams */
|
/* allow chaining of (unseekable) streams */
|
||||||
@@ -311,7 +264,7 @@ MPDOpusDecoder::HandleEOS()
|
|||||||
throw StopDecoder();
|
throw StopDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DecoderCommand
|
inline void
|
||||||
MPDOpusDecoder::HandleTags(const ogg_packet &packet)
|
MPDOpusDecoder::HandleTags(const ogg_packet &packet)
|
||||||
{
|
{
|
||||||
ReplayGainInfo rgi;
|
ReplayGainInfo rgi;
|
||||||
@@ -319,7 +272,6 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
|
|||||||
|
|
||||||
TagBuilder tag_builder;
|
TagBuilder tag_builder;
|
||||||
|
|
||||||
DecoderCommand cmd;
|
|
||||||
if (ScanOpusTags(packet.packet, packet.bytes,
|
if (ScanOpusTags(packet.packet, packet.bytes,
|
||||||
&rgi,
|
&rgi,
|
||||||
add_tag_handler, &tag_builder) &&
|
add_tag_handler, &tag_builder) &&
|
||||||
@@ -327,14 +279,13 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
|
|||||||
decoder_replay_gain(decoder, &rgi);
|
decoder_replay_gain(decoder, &rgi);
|
||||||
|
|
||||||
Tag tag = tag_builder.Commit();
|
Tag tag = tag_builder.Commit();
|
||||||
cmd = decoder_tag(decoder, input_stream, std::move(tag));
|
auto cmd = decoder_tag(decoder, input_stream, std::move(tag));
|
||||||
} else
|
if (cmd != DecoderCommand::NONE)
|
||||||
cmd = decoder_get_command(decoder);
|
throw cmd;
|
||||||
|
}
|
||||||
return cmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DecoderCommand
|
inline void
|
||||||
MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
|
MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
|
||||||
{
|
{
|
||||||
assert(opus_decoder != nullptr);
|
assert(opus_decoder != nullptr);
|
||||||
@@ -354,15 +305,13 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
|
|||||||
output_buffer, nbytes,
|
output_buffer, nbytes,
|
||||||
0);
|
0);
|
||||||
if (cmd != DecoderCommand::NONE)
|
if (cmd != DecoderCommand::NONE)
|
||||||
return cmd;
|
throw cmd;
|
||||||
|
|
||||||
if (packet.granulepos > 0)
|
if (packet.granulepos > 0)
|
||||||
decoder_timestamp(decoder,
|
decoder_timestamp(decoder,
|
||||||
double(packet.granulepos)
|
double(packet.granulepos)
|
||||||
/ opus_sample_rate);
|
/ opus_sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DecoderCommand::NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -380,7 +329,11 @@ MPDOpusDecoder::Seek(uint64_t where_frame)
|
|||||||
offset_type offset(where_granulepos * input_stream.GetSize()
|
offset_type offset(where_granulepos * input_stream.GetSize()
|
||||||
/ eos_granulepos);
|
/ eos_granulepos);
|
||||||
|
|
||||||
return OggSeekPageAtOffset(oy, os, input_stream, offset);
|
if (!input_stream.LockSeek(offset, IgnoreError()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PostSeek();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -399,21 +352,18 @@ mpd_opus_stream_decode(Decoder &decoder,
|
|||||||
MPDOpusDecoder d(reader);
|
MPDOpusDecoder d(reader);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
auto cmd = d.HandlePackets();
|
try {
|
||||||
if (cmd == DecoderCommand::SEEK) {
|
d.Visit();
|
||||||
if (d.Seek(decoder_seek_where_frame(decoder)))
|
break;
|
||||||
decoder_command_finished(decoder);
|
} catch (DecoderCommand cmd) {
|
||||||
else
|
if (cmd == DecoderCommand::SEEK) {
|
||||||
decoder_seek_error(decoder);
|
if (d.Seek(decoder_seek_where_frame(decoder)))
|
||||||
|
decoder_command_finished(decoder);
|
||||||
continue;
|
else
|
||||||
|
decoder_seek_error(decoder);
|
||||||
|
} else if (cmd != DecoderCommand::NONE)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd != DecoderCommand::NONE)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!d.ReadNextPage())
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
101
src/lib/xiph/OggVisitor.cxx
Normal file
101
src/lib/xiph/OggVisitor.cxx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2016 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 "OggVisitor.hxx"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
void
|
||||||
|
OggVisitor::EndStream()
|
||||||
|
{
|
||||||
|
if (!has_stream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
has_stream = false;
|
||||||
|
OnOggEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
OggVisitor::ReadNextPage()
|
||||||
|
{
|
||||||
|
ogg_page page;
|
||||||
|
if (!sync.ExpectPage(page))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto page_serialno = ogg_page_serialno(&page);
|
||||||
|
if (page_serialno != stream.GetSerialNo()) {
|
||||||
|
EndStream();
|
||||||
|
stream.Reinitialize(page_serialno);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.PageIn(page);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
OggVisitor::HandlePacket(const ogg_packet &packet)
|
||||||
|
{
|
||||||
|
if (packet.b_o_s) {
|
||||||
|
EndStream();
|
||||||
|
has_stream = true;
|
||||||
|
OnOggBeginning(packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_stream)
|
||||||
|
/* fail if BOS is missing */
|
||||||
|
throw std::runtime_error("BOS packet expected");
|
||||||
|
|
||||||
|
if (packet.e_o_s) {
|
||||||
|
EndStream();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnOggPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
OggVisitor::HandlePackets()
|
||||||
|
{
|
||||||
|
ogg_packet packet;
|
||||||
|
while (stream.PacketOut(packet) == 1)
|
||||||
|
HandlePacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OggVisitor::Visit()
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
HandlePackets();
|
||||||
|
} while (ReadNextPage());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OggVisitor::PostSeek()
|
||||||
|
{
|
||||||
|
sync.Reset();
|
||||||
|
|
||||||
|
/* reset the stream to clear any previous partial packet
|
||||||
|
data */
|
||||||
|
stream.Reset();
|
||||||
|
|
||||||
|
/* find the next Ogg page and feed it into the stream */
|
||||||
|
sync.ExpectPageSeekIn(stream);
|
||||||
|
}
|
70
src/lib/xiph/OggVisitor.hxx
Normal file
70
src/lib/xiph/OggVisitor.hxx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2016 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_VISITOR_HXX
|
||||||
|
#define MPD_OGG_VISITOR_HXX
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "OggSyncState.hxx"
|
||||||
|
#include "OggStreamState.hxx"
|
||||||
|
|
||||||
|
#include <ogg/ogg.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
class Reader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class which iterates over Ogg packets in a #Reader.
|
||||||
|
* Subclass it and implement the virtual methods.
|
||||||
|
*/
|
||||||
|
class OggVisitor {
|
||||||
|
OggSyncState sync;
|
||||||
|
OggStreamState stream;
|
||||||
|
|
||||||
|
bool has_stream = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit OggVisitor(Reader &reader)
|
||||||
|
:sync(reader), stream(0) {}
|
||||||
|
|
||||||
|
long GetSerialNo() const {
|
||||||
|
return stream.GetSerialNo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this method after seeking the #Reader.
|
||||||
|
*/
|
||||||
|
void PostSeek();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void EndStream();
|
||||||
|
bool ReadNextPage();
|
||||||
|
void HandlePacket(const ogg_packet &packet);
|
||||||
|
void HandlePackets();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void OnOggBeginning(const ogg_packet &packet) = 0;
|
||||||
|
virtual void OnOggPacket(const ogg_packet &packet) = 0;
|
||||||
|
virtual void OnOggEnd() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user