Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
2ee43c40
Commit
2ee43c40
authored
May 14, 2016
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
decoder/vorbis: reimplement using class OggDecoder
Use libvorbis instead of libvorbisfile, which gives us more control over the decoding process.
parent
24fa3f5e
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
217 additions
and
224 deletions
+217
-224
configure.ac
configure.ac
+1
-1
VorbisDecoderPlugin.cxx
src/decoder/plugins/VorbisDecoderPlugin.cxx
+216
-223
No files found.
configure.ac
View file @
2ee43c40
...
@@ -964,7 +964,7 @@ if test x$enable_tremor = xyes; then
...
@@ -964,7 +964,7 @@ if test x$enable_tremor = xyes; then
fi
fi
fi
fi
MPD_AUTO_PKG(vorbis, VORBIS, [vorbis
file vorbis
ogg],
MPD_AUTO_PKG(vorbis, VORBIS, [vorbis ogg],
[Ogg Vorbis decoder], [libvorbis not found])
[Ogg Vorbis decoder], [libvorbis not found])
if test x$enable_vorbis = xyes; then
if test x$enable_vorbis = xyes; then
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support])
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support])
...
...
src/decoder/plugins/VorbisDecoderPlugin.cxx
View file @
2ee43c40
...
@@ -19,9 +19,8 @@
...
@@ -19,9 +19,8 @@
#include "config.h"
#include "config.h"
#include "VorbisDecoderPlugin.h"
#include "VorbisDecoderPlugin.h"
#include "OggDecoder.hxx"
#include "lib/xiph/VorbisComments.hxx"
#include "lib/xiph/VorbisComments.hxx"
#include "lib/xiph/OggSyncState.hxx"
#include "lib/xiph/OggStreamState.hxx"
#include "lib/xiph/OggPacket.hxx"
#include "lib/xiph/OggPacket.hxx"
#include "lib/xiph/OggFind.hxx"
#include "lib/xiph/OggFind.hxx"
#include "VorbisDomain.hxx"
#include "VorbisDomain.hxx"
...
@@ -38,160 +37,259 @@
...
@@ -38,160 +37,259 @@
#include "Log.hxx"
#include "Log.hxx"
#ifndef HAVE_TREMOR
#ifndef HAVE_TREMOR
#define OV_EXCLUDE_STATIC_CALLBACKS
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#else
#else
#include <tremor/ivorbisfile.h>
#include <tremor/ivorbiscodec.h>
/* Macros to make Tremor's API look like libogg. Tremor always
returns host-byte-order 16-bit signed data, and uses integer
milliseconds where libogg uses double seconds.
*/
#define ov_read(VF, BUFFER, LENGTH, BIGENDIANP, WORD, SGNED, BITSTREAM) \
ov_read(VF, BUFFER, LENGTH, BITSTREAM)
#define ov_time_total(VF, I) ((double)ov_time_total(VF, I)/1000)
#define ov_time_tell(VF) ((double)ov_time_tell(VF)/1000)
#define ov_time_seek_page(VF, S) (ov_time_seek_page(VF, (S)*1000))
#endif
/* HAVE_TREMOR */
#endif
/* HAVE_TREMOR */
#include <
errno.h
>
#include <
stdexcept
>
struct
VorbisInputStream
{
class
VorbisDecoder
final
:
public
OggDecoder
{
Decoder
*
const
decoder
;
#ifdef HAVE_TREMOR
static
constexpr
SampleFormat
sample_format
=
SampleFormat
::
S16
;
typedef
ogg_int32_t
in_sample_t
;
typedef
int16_t
out_sample_t
;
#else
static
constexpr
SampleFormat
sample_format
=
SampleFormat
::
FLOAT
;
typedef
float
in_sample_t
;
typedef
float
out_sample_t
;
#endif
InputStream
&
input_stream
;
unsigned
remaining_header_packets
;
bool
seekable
;
VorbisInputStream
(
Decoder
*
_decoder
,
InputStream
&
_is
)
vorbis_info
vi
;
:
decoder
(
_decoder
),
input_stream
(
_is
),
vorbis_comment
vc
;
seekable
(
input_stream
.
CheapSeeking
())
{}
vorbis_dsp_state
dsp
;
}
;
vorbis_block
block
;
static
size_t
ogg_read_cb
(
void
*
ptr
,
size_t
size
,
size_t
nmemb
,
void
*
data
)
/**
{
* If non-zero, then a previous Vorbis stream has been found
VorbisInputStream
*
vis
=
(
VorbisInputStream
*
)
data
;
* already with this number of channels.
size_t
ret
=
decoder_read
(
vis
->
decoder
,
vis
->
input_stream
,
*/
ptr
,
size
*
nmemb
);
AudioFormat
audio_format
=
AudioFormat
::
Undefined
();
size_t
frame_size
;
errno
=
0
;
bool
dsp_initialized
=
false
;
return
ret
/
size
;
public
:
}
explicit
VorbisDecoder
(
DecoderReader
&
reader
)
:
OggDecoder
(
reader
)
{
InitVorbis
();
}
static
int
ogg_seek_cb
(
void
*
data
,
ogg_int64_t
_offset
,
int
whence
)
~
VorbisDecoder
()
{
{
DeinitVorbis
();
VorbisInputStream
*
vis
=
(
VorbisInputStream
*
)
data
;
}
InputStream
&
is
=
vis
->
input_stream
;
if
(
!
vis
->
seekable
||
bool
Seek
(
uint64_t
where_frame
);
(
vis
->
decoder
!=
nullptr
&&
decoder_get_command
(
*
vis
->
decoder
)
==
DecoderCommand
::
STOP
))
return
-
1
;
offset_type
offset
=
_offset
;
private
:
switch
(
whence
)
{
void
InitVorbis
()
{
case
SEEK_SET
:
vorbis_info_init
(
&
vi
);
break
;
vorbis_comment_init
(
&
vc
);
}
case
SEEK_CUR
:
void
DeinitVorbis
()
{
offset
+=
is
.
GetOffset
();
if
(
dsp_initialized
)
{
break
;
dsp_initialized
=
false
;
case
SEEK_END
:
vorbis_block_clear
(
&
block
);
if
(
!
is
.
KnownSize
())
vorbis_dsp_clear
(
&
dsp
);
return
-
1
;
}
offset
+=
is
.
GetSize
();
vorbis_comment_clear
(
&
vc
);
break
;
vorbis_info_clear
(
&
vi
);
}
default:
void
ReinitVorbis
()
{
return
-
1
;
DeinitVorbis
();
InitVorbis
();
}
}
return
is
.
LockSeek
(
offset
,
IgnoreError
())
void
SubmitInit
();
?
0
:
-
1
;
bool
SubmitSomePcm
()
;
}
void
SubmitPcm
();
/* TODO: check Ogg libraries API and see if we can just not have this func */
protected
:
static
int
ogg_close_cb
(
gcc_unused
void
*
data
)
/* virtual methods from class OggVisitor */
void
OnOggBeginning
(
const
ogg_packet
&
packet
)
override
;
void
OnOggPacket
(
const
ogg_packet
&
packet
)
override
;
void
OnOggEnd
()
override
;
};
bool
VorbisDecoder
::
Seek
(
uint64_t
where_frame
)
{
{
return
0
;
assert
(
IsSeekable
());
assert
(
input_stream
.
IsSeekable
());
assert
(
input_stream
.
KnownSize
());
const
ogg_int64_t
where_granulepos
(
where_frame
);
if
(
!
SeekGranulePos
(
where_granulepos
,
IgnoreError
()))
return
false
;
vorbis_synthesis_restart
(
&
dsp
);
return
true
;
}
}
static
long
ogg_tell_cb
(
void
*
data
)
void
VorbisDecoder
::
OnOggBeginning
(
const
ogg_packet
&
_packet
)
{
{
VorbisInputStream
*
vis
=
(
VorbisInputStream
*
)
data
;
/* libvorbis wants non-const packets */
ogg_packet
&
packet
=
const_cast
<
ogg_packet
&>
(
_packet
);
return
(
long
)
vis
->
input_stream
.
GetOffset
();
ReinitVorbis
();
}
static
const
ov_callbacks
vorbis_is_callbacks
=
{
if
(
vorbis_synthesis_headerin
(
&
vi
,
&
vc
,
&
packet
)
!=
0
)
ogg_read_cb
,
throw
std
::
runtime_error
(
"Unrecognized Vorbis BOS packet"
);
ogg_seek_cb
,
ogg_close_cb
,
ogg_tell_cb
,
};
static
const
char
*
remaining_header_packets
=
2
;
vorbis_strerror
(
int
code
)
}
static
void
vorbis_send_comments
(
Decoder
&
decoder
,
InputStream
&
is
,
char
**
comments
)
{
{
switch
(
code
)
{
Tag
*
tag
=
vorbis_comments_to_tag
(
comments
);
case
OV_EREAD
:
if
(
!
tag
)
return
"read error"
;
return
;
case
OV_ENOTVORBIS
:
decoder_tag
(
decoder
,
is
,
std
::
move
(
*
tag
));
return
"not vorbis stream"
;
delete
tag
;
}
case
OV_EVERSION
:
void
return
"vorbis version mismatch"
;
VorbisDecoder
::
SubmitInit
()
{
assert
(
!
dsp_initialized
);
case
OV_EBADHEADER
:
Error
error
;
return
"invalid vorbis header"
;
if
(
!
audio_format_init_checked
(
audio_format
,
vi
.
rate
,
sample_format
,
vi
.
channels
,
error
))
throw
std
::
runtime_error
(
error
.
GetMessage
());
case
OV_EFAULT
:
frame_size
=
audio_format
.
GetFrameSize
();
return
"internal logic error"
;
default:
const
auto
eos_granulepos
=
UpdateEndGranulePos
();
return
"unknown error"
;
const
auto
duration
=
eos_granulepos
>=
0
}
?
SignedSongTime
::
FromScale
<
uint64_t
>
(
eos_granulepos
,
audio_format
.
sample_rate
)
:
SignedSongTime
::
Negative
();
decoder_initialized
(
decoder
,
audio_format
,
eos_granulepos
>
0
,
duration
);
}
}
static
bool
bool
vorbis_is_open
(
VorbisInputStream
*
vis
,
OggVorbis_File
*
vf
)
VorbisDecoder
::
SubmitSomePcm
(
)
{
{
int
ret
=
ov_open_callbacks
(
vis
,
vf
,
nullptr
,
0
,
vorbis_is_callbacks
);
in_sample_t
**
pcm
;
if
(
ret
<
0
)
{
int
result
=
vorbis_synthesis_pcmout
(
&
dsp
,
&
pcm
);
if
(
vis
->
decoder
==
nullptr
||
if
(
result
<=
0
)
decoder_get_command
(
*
vis
->
decoder
)
==
DecoderCommand
::
NONE
)
FormatWarning
(
vorbis_domain
,
"Failed to open Ogg Vorbis stream: %s"
,
vorbis_strerror
(
ret
));
return
false
;
return
false
;
out_sample_t
buffer
[
4096
];
const
unsigned
channels
=
audio_format
.
channels
;
size_t
max_frames
=
ARRAY_SIZE
(
buffer
)
/
channels
;
size_t
n_frames
=
std
::
min
(
size_t
(
result
),
max_frames
);
#ifdef HAVE_TREMOR
for
(
unsigned
c
=
0
;
c
<
channels
;
++
c
)
{
const
auto
*
src
=
pcm
[
c
];
auto
*
dest
=
&
buffer
[
c
];
for
(
size_t
i
=
0
;
i
<
n_frames
;
++
i
)
{
*
dest
=
*
src
++
;
dest
+=
channels
;
}
}
}
#else
PcmInterleaveFloat
(
buffer
,
ConstBuffer
<
const
in_sample_t
*>
(
pcm
,
channels
),
n_frames
);
#endif
vorbis_synthesis_read
(
&
dsp
,
n_frames
);
const
size_t
nbytes
=
n_frames
*
frame_size
;
auto
cmd
=
decoder_data
(
decoder
,
input_stream
,
buffer
,
nbytes
,
0
);
if
(
cmd
!=
DecoderCommand
::
NONE
)
throw
cmd
;
return
true
;
return
true
;
}
}
static
void
void
vorbis_send_comments
(
Decoder
&
decoder
,
InputStream
&
is
,
VorbisDecoder
::
SubmitPcm
()
char
**
comments
)
{
{
Tag
*
tag
=
vorbis_comments_to_tag
(
comments
);
while
(
SubmitSomePcm
())
{}
if
(
!
tag
)
}
void
VorbisDecoder
::
OnOggPacket
(
const
ogg_packet
&
_packet
)
{
/* libvorbis wants non-const packets */
ogg_packet
&
packet
=
const_cast
<
ogg_packet
&>
(
_packet
);
if
(
remaining_header_packets
>
0
)
{
if
(
vorbis_synthesis_headerin
(
&
vi
,
&
vc
,
&
packet
)
!=
0
)
throw
std
::
runtime_error
(
"Unrecognized Vorbis header packet"
);
if
(
--
remaining_header_packets
>
0
)
return
;
return
;
decoder_tag
(
decoder
,
is
,
std
::
move
(
*
tag
));
if
(
audio_format
.
IsDefined
())
{
delete
tag
;
/* TODO: change the MPD decoder plugin API to
}
allow mid-song AudioFormat changes */
if
((
unsigned
)
vi
.
rate
!=
audio_format
.
sample_rate
||
(
unsigned
)
vi
.
channels
!=
audio_format
.
channels
)
throw
std
::
runtime_error
(
"Next stream has different audio format"
);
}
else
SubmitInit
();
vorbis_send_comments
(
decoder
,
input_stream
,
vc
.
user_comments
);
ReplayGainInfo
rgi
;
if
(
vorbis_comments_to_replay_gain
(
rgi
,
vc
.
user_comments
))
decoder_replay_gain
(
decoder
,
&
rgi
);
}
else
{
if
(
!
dsp_initialized
)
{
dsp_initialized
=
true
;
vorbis_synthesis_init
(
&
dsp
,
&
vi
);
vorbis_block_init
(
&
dsp
,
&
block
);
}
if
(
vorbis_synthesis
(
&
block
,
&
packet
)
!=
0
)
{
/* ignore bad packets, but give the MPD core a
chance to stop us */
auto
cmd
=
decoder_get_command
(
decoder
);
if
(
cmd
!=
DecoderCommand
::
NONE
)
throw
cmd
;
return
;
}
if
(
vorbis_synthesis_blockin
(
&
dsp
,
&
block
)
!=
0
)
throw
std
::
runtime_error
(
"vorbis_synthesis_blockin() failed"
);
SubmitPcm
();
#ifndef HAVE_TREMOR
#ifndef HAVE_TREMOR
static
void
if
(
packet
.
granulepos
>
0
)
vorbis_interleave
(
float
*
dest
,
const
float
*
const
*
src
,
decoder_timestamp
(
decoder
,
unsigned
nframes
,
unsigned
channels
)
vorbis_granule_time
(
&
dsp
,
packet
.
granulepos
));
#endif
}
}
void
VorbisDecoder
::
OnOggEnd
()
{
{
PcmInterleaveFloat
(
dest
,
ConstBuffer
<
const
float
*>
(
src
,
channels
),
nframes
);
}
}
#endif
/* public */
/* public */
...
@@ -204,16 +302,6 @@ vorbis_init(gcc_unused const ConfigBlock &block)
...
@@ -204,16 +302,6 @@ vorbis_init(gcc_unused const ConfigBlock &block)
return
true
;
return
true
;
}
}
gcc_pure
static
SignedSongTime
vorbis_duration
(
OggVorbis_File
&
vf
)
{
auto
total
=
ov_time_total
(
&
vf
,
-
1
);
return
total
>=
0
?
SignedSongTime
::
FromS
(
total
)
:
SignedSongTime
::
Negative
();
}
static
void
static
void
vorbis_stream_decode
(
Decoder
&
decoder
,
vorbis_stream_decode
(
Decoder
&
decoder
,
InputStream
&
input_stream
)
InputStream
&
input_stream
)
...
@@ -225,118 +313,23 @@ vorbis_stream_decode(Decoder &decoder,
...
@@ -225,118 +313,23 @@ vorbis_stream_decode(Decoder &decoder,
moved it */
moved it */
input_stream
.
LockRewind
(
IgnoreError
());
input_stream
.
LockRewind
(
IgnoreError
());
VorbisInputStream
vis
(
&
decoder
,
input_stream
);
DecoderReader
reader
(
decoder
,
input_stream
);
OggVorbis_File
vf
;
VorbisDecoder
d
(
reader
);
if
(
!
vorbis_is_open
(
&
vis
,
&
vf
))
return
;
const
vorbis_info
*
vi
=
ov_info
(
&
vf
,
-
1
);
while
(
true
)
{
if
(
vi
==
nullptr
)
{
try
{
LogWarning
(
vorbis_domain
,
"ov_info() has failed"
);
d
.
Visit
();
return
;
break
;
}
}
catch
(
DecoderCommand
cmd
)
{
Error
error
;
AudioFormat
audio_format
;
if
(
!
audio_format_init_checked
(
audio_format
,
vi
->
rate
,
#ifdef HAVE_TREMOR
SampleFormat
::
S16
,
#else
SampleFormat
::
FLOAT
,
#endif
vi
->
channels
,
error
))
{
LogError
(
error
);
return
;
}
decoder_initialized
(
decoder
,
audio_format
,
vis
.
seekable
,
vorbis_duration
(
vf
));
#ifdef HAVE_TREMOR
char
buffer
[
4096
];
#else
float
buffer
[
2048
];
const
int
frames_per_buffer
=
ARRAY_SIZE
(
buffer
)
/
audio_format
.
channels
;
const
unsigned
frame_size
=
sizeof
(
buffer
[
0
])
*
audio_format
.
channels
;
#endif
int
prev_section
=
-
1
;
unsigned
kbit_rate
=
0
;
DecoderCommand
cmd
=
decoder_get_command
(
decoder
);
while
(
cmd
!=
DecoderCommand
::
STOP
)
{
if
(
cmd
==
DecoderCommand
::
SEEK
)
{
if
(
cmd
==
DecoderCommand
::
SEEK
)
{
auto
seek_where
=
decoder_seek_where_frame
(
decoder
);
if
(
d
.
Seek
(
decoder_seek_where_frame
(
decoder
)))
if
(
0
==
ov_pcm_seek_page
(
&
vf
,
seek_where
))
{
decoder_command_finished
(
decoder
);
decoder_command_finished
(
decoder
);
}
else
else
decoder_seek_error
(
decoder
);
decoder_seek_error
(
decoder
);
}
}
else
if
(
cmd
!=
DecoderCommand
::
NONE
)
int
current_section
;
#ifdef HAVE_TREMOR
long
nbytes
=
ov_read
(
&
vf
,
buffer
,
sizeof
(
buffer
),
IsBigEndian
(),
2
,
1
,
&
current_section
);
#else
float
**
per_channel
;
long
nframes
=
ov_read_float
(
&
vf
,
&
per_channel
,
frames_per_buffer
,
&
current_section
);
long
nbytes
=
nframes
;
if
(
nframes
>
0
)
{
vorbis_interleave
(
buffer
,
(
const
float
*
const
*
)
per_channel
,
nframes
,
audio_format
.
channels
);
nbytes
*=
frame_size
;
}
#endif
if
(
nbytes
==
OV_HOLE
)
/* bad packet */
nbytes
=
0
;
else
if
(
nbytes
<=
0
)
/* break on EOF or other error */
break
;
if
(
current_section
!=
prev_section
)
{
vi
=
ov_info
(
&
vf
,
-
1
);
if
(
vi
==
nullptr
)
{
LogWarning
(
vorbis_domain
,
"ov_info() has failed"
);
break
;
}
if
(
vi
->
rate
!=
(
long
)
audio_format
.
sample_rate
||
vi
->
channels
!=
(
int
)
audio_format
.
channels
)
{
/* we don't support audio format
change yet */
LogWarning
(
vorbis_domain
,
"audio format change, stopping here"
);
break
;
break
;
}
}
char
**
comments
=
ov_comment
(
&
vf
,
-
1
)
->
user_comments
;
vorbis_send_comments
(
decoder
,
input_stream
,
comments
);
ReplayGainInfo
rgi
;
if
(
vorbis_comments_to_replay_gain
(
rgi
,
comments
))
decoder_replay_gain
(
decoder
,
&
rgi
);
prev_section
=
current_section
;
}
long
test
=
ov_bitrate_instant
(
&
vf
);
if
(
test
>
0
)
kbit_rate
=
test
/
1000
;
cmd
=
decoder_data
(
decoder
,
input_stream
,
buffer
,
nbytes
,
kbit_rate
);
}
}
ov_clear
(
&
vf
);
}
}
static
void
static
void
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment