Skip to content

Commit 706e625

Browse files
committed
Merge branch 'v0.24.x'
2 parents 58d386a + 5101bad commit 706e625

13 files changed

Lines changed: 94 additions & 22 deletions

File tree

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ver 0.25 (not yet released)
1313
ver 0.24.6 (not yet released)
1414
* decoder
1515
- mpg123: another workaround for libmpg123 ID3 corruption bug
16+
- opus: fix seeking in files with large OpusTags
1617

1718
ver 0.24.5 (2025/07/31)
1819
* database

src/decoder/plugins/OggDecoder.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ OggDecoder::SeekGranulePos(ogg_int64_t where_granulepos)
7575
static const ogg_int64_t MARGIN_BEFORE = 44100 / 3;
7676
static const ogg_int64_t MARGIN_AFTER = 44100 / 10;
7777

78-
offset_type min_offset = 0, max_offset = input_stream.GetSize();
78+
offset_type min_offset = first_offset, max_offset = input_stream.GetSize();
7979
ogg_int64_t min_granule = 0, max_granule = end_granulepos;
8080

8181
while (true) {

src/decoder/plugins/OggDecoder.hxx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,23 @@
99
#include "input/Offset.hxx"
1010

1111
class OggDecoder : public OggVisitor {
12+
/**
13+
* The file offset of the first audio packet (starts at
14+
* granulepos 0). This is used by SeekGranulePos() to
15+
* interpolate the seek offset between this offset and
16+
* end-of-file, possibly skipping (large) tags preceding the
17+
* first audio packet.
18+
*/
19+
offset_type first_offset = 0;
20+
21+
/**
22+
* The granulepos at the end of the last packet. This is used
23+
* to calculate the song duration and to calculate seek file
24+
* offsets.
25+
*
26+
* This field is uninitialized until UpdateEndGranulePos() is
27+
* called.
28+
*/
1229
ogg_int64_t end_granulepos;
1330

1431
protected:
@@ -30,6 +47,25 @@ private:
3047
ogg_int64_t LoadEndGranulePos() const;
3148

3249
protected:
50+
bool HasFirstOffset() const noexcept {
51+
return first_offset > 0;
52+
}
53+
54+
void SetFirstOffset(offset_type _first_offset) noexcept {
55+
first_offset = _first_offset;
56+
}
57+
58+
/**
59+
* If currently unset, set the #first_offset field to the
60+
* start of the most recent Ogg page. Decoder implementations
61+
* should call this when they see the first page/packet
62+
* containing audio data.
63+
*/
64+
void AutoSetFirstOffset() noexcept {
65+
if (!HasFirstOffset())
66+
SetFirstOffset(GetStartOffset());
67+
}
68+
3369
ogg_int64_t UpdateEndGranulePos() {
3470
return end_granulepos = LoadEndGranulePos();
3571
}

src/decoder/plugins/OpusDecoderPlugin.cxx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ class MPDOpusDecoder final : public OggDecoder {
113113
*/
114114
bool submitted_replay_gain = false;
115115

116+
/**
117+
* This is true if a SEEK command was received from
118+
* DecoderClient::Ready(). We continue reading until we see
119+
* the first packet in order to apply ReplayGain and find the
120+
* file offset of the first audio packet.
121+
*/
122+
bool initial_seek = false;
123+
116124
public:
117125
explicit MPDOpusDecoder(DecoderReader &reader)
118126
:OggDecoder(reader) {}
@@ -233,8 +241,17 @@ MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet)
233241
* audio_format.channels];
234242

235243
auto cmd = client.GetCommand();
236-
if (cmd != DecoderCommand::NONE)
244+
if (cmd != DecoderCommand::NONE) {
245+
if (cmd == DecoderCommand::SEEK) {
246+
/* postpone the seek until we have found the
247+
first packet with audio data, i.e. after
248+
the AutoSetFirstOffset() call */
249+
initial_seek = true;
250+
return;
251+
}
252+
237253
throw cmd;
254+
}
238255
}
239256

240257
void
@@ -277,7 +294,8 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
277294
if (!tag_builder.empty()) {
278295
Tag tag = tag_builder.Commit();
279296
auto cmd = client.SubmitTag(input_stream, std::move(tag));
280-
if (cmd != DecoderCommand::NONE)
297+
if (cmd != DecoderCommand::NONE &&
298+
(cmd != DecoderCommand::SEEK || !initial_seek))
281299
throw cmd;
282300
}
283301
}
@@ -299,6 +317,16 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
299317
submitted_replay_gain = true;
300318
}
301319

320+
AutoSetFirstOffset();
321+
322+
if (initial_seek) {
323+
/* now that AutoSetFirstOffset() was called, we can do
324+
the seek (don't bother to waste time on decoding
325+
audio data) */
326+
initial_seek = false;
327+
throw DecoderCommand::SEEK;
328+
}
329+
302330
int nframes = opus_decode(opus_decoder,
303331
(const unsigned char*)packet.packet,
304332
packet.bytes,

src/decoder/plugins/VorbisDecoderPlugin.cxx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ VorbisDecoder::OnOggPacket(const ogg_packet &_packet)
266266
vorbis_block_init(&dsp, &block);
267267
}
268268

269+
AutoSetFirstOffset();
270+
269271
if (vorbis_synthesis(&block, &packet) != 0) {
270272
/* ignore bad packets, but give the MPD core a
271273
chance to stop us */

src/event/Loop.cxx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,12 @@ EventLoop::UringWait(Event::Duration timeout) noexcept
411411
io_uring_prep_poll_multishot() is edge-triggered, so we
412412
have to consume all events to rearm it */
413413

414-
if (!epoll_ready) {
414+
if (epoll_ready)
415+
/* don't wait for io_uring completions if epoll is
416+
still ready */
417+
timeout = Event::Duration{0};
418+
419+
{
415420
struct __kernel_timespec timeout_buffer;
416421
auto *kernel_timeout = ExportTimeoutKernelTimespec(timeout, timeout_buffer);
417422
Uring::Queue &uring_queue = *uring;

src/lib/xiph/OggSyncState.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ OggSyncState::ExpectPageIn(ogg_stream_state &os)
5151
bool
5252
OggSyncState::ExpectPageSeek(ogg_page &page)
5353
{
54-
size_t remaining_skipped = 65536;
54+
size_t remaining_skipped = 1024 * 1024;
5555

5656
while (true) {
5757
int r = ogg_sync_pageseek(&oy, &page);

src/neighbor/Glue.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ NeighborGlue::Open()
5757
for (auto k = explorers.begin(); k != i; ++k)
5858
k->explorer->Close();
5959

60-
std::throw_with_nested(FmtRuntimeError("Failed to open neighblor plugin {:?}",
60+
std::throw_with_nested(FmtRuntimeError("Failed to open neighbor plugin {:?}",
6161
i->name));
6262
}
6363
}

subprojects/flac.wrap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ directory = flac-1.5.0
33
source_url = https://github.com/xiph/flac/releases/download/1.5.0/flac-1.5.0.tar.xz
44
source_filename = flac-1.5.0.tar.xz
55
source_hash = f2c1c76592a82ffff8413ba3c4a1299b6c7ab06c734dee03fd88630485c2b920
6-
patch_filename = flac_1.5.0-1_patch.zip
7-
patch_url = https://wrapdb.mesonbuild.com/v2/flac_1.5.0-1/get_patch
8-
patch_hash = c3d7dcb01d6e0a2bc066b12e789661fd9c4356500787004cfc1950d52babd6ca
9-
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/flac_1.5.0-1/flac-1.5.0.tar.xz
10-
wrapdb_version = 1.5.0-1
6+
patch_filename = flac_1.5.0-2_patch.zip
7+
patch_url = https://wrapdb.mesonbuild.com/v2/flac_1.5.0-2/get_patch
8+
patch_hash = ee4cd8175784b4032e3b19dda1fe4c1d6d4b285dfa87fe94361f1a6fde732ff0
9+
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/flac_1.5.0-2/flac-1.5.0.tar.xz
10+
wrapdb_version = 1.5.0-2
1111

1212
[provide]
1313
flac = flac_dep

subprojects/gtest.wrap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ directory = googletest-1.17.0
33
source_url = https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz
44
source_filename = googletest-1.17.0.tar.gz
55
source_hash = 65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c
6-
patch_filename = gtest_1.17.0-3_patch.zip
7-
patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.17.0-3/get_patch
8-
patch_hash = 3e2799683f27c6dce138b7bae823416581c467ddde755c9a516c0863225f0ceb
9-
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/gtest_1.17.0-3/googletest-1.17.0.tar.gz
10-
wrapdb_version = 1.17.0-3
6+
patch_filename = gtest_1.17.0-4_patch.zip
7+
patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.17.0-4/get_patch
8+
patch_hash = 3abf7662d09db706453a5b064a1e914678c74b9d9b0b19382747ca561d0d8750
9+
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/gtest_1.17.0-4/googletest-1.17.0.tar.gz
10+
wrapdb_version = 1.17.0-4
1111

1212
[provide]
1313
gtest = gtest_dep

0 commit comments

Comments
 (0)