Skip to content

Commit 36b0228

Browse files
committed
[BH-2100] Fix crash when seeking in large MP3 file
* Fix of the issue that seeking in large MP3 file would cause OOM error when creating MP3 indexes. This caused OS crash when looping large MP3 file in Relaxation. * Minor cleanups.
1 parent e84aa4a commit 36b0228

12 files changed

+155
-130
lines changed

harmony_changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
### Fixed
6+
* Fixed crash in Relaxation when playing large MP3 file
67

78
### Added
89

module-audio/Audio/Operation/PlaybackOperation.cpp

+21-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
1+
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
22
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md
33

44
#include "PlaybackOperation.hpp"
@@ -16,8 +16,8 @@ namespace audio
1616
using namespace AudioServiceMessage;
1717

1818
PlaybackOperation::PlaybackOperation(const std::string &filePath,
19-
const audio::PlaybackType &playbackType,
20-
const audio::PlaybackMode &playbackMode,
19+
const PlaybackType &playbackType,
20+
const PlaybackMode &playbackMode,
2121
Callback callback)
2222
: Operation(std::move(callback), playbackType), playbackMode(playbackMode), dec(nullptr)
2323
{
@@ -27,13 +27,13 @@ namespace audio
2727
AddProfile(Profile::Type::PlaybackLoudspeaker, playbackType, true);
2828

2929
endOfFileCallback = [this]() {
30-
if (this->playbackMode == audio::PlaybackMode::Single) {
30+
if (this->playbackMode == PlaybackMode::Single) {
3131
state = State::Idle;
3232
const auto msg = AudioServiceMessage::EndOfFile(operationToken);
3333
serviceCallback(&msg);
3434
}
3535
else {
36-
dec->setPosition(playbackStartPosition);
36+
dec->rewind();
3737
}
3838
};
3939

@@ -56,7 +56,7 @@ namespace audio
5656
}
5757
}
5858

59-
audio::RetCode PlaybackOperation::Start(audio::Token token)
59+
RetCode PlaybackOperation::Start(Token token)
6060
{
6161
if (state == State::Active || (state == State::Paused && outputConnection != nullptr)) {
6262
return RetCode::InvokedInIncorrectState;
@@ -69,7 +69,7 @@ namespace audio
6969
}
7070
catch (std::invalid_argument &e) {
7171
LOG_FATAL("Cannot create audio stream: %s", e.what());
72-
return audio::RetCode::Failed;
72+
return RetCode::Failed;
7373
}
7474

7575
// create audio connection
@@ -89,11 +89,11 @@ namespace audio
8989
return GetDeviceError(ret);
9090
}
9191

92-
audio::RetCode PlaybackOperation::Stop()
92+
RetCode PlaybackOperation::Stop()
9393
{
9494
state = State::Idle;
9595
if (!audioDevice) {
96-
return audio::RetCode::DeviceFailure;
96+
return RetCode::DeviceFailure;
9797
}
9898

9999
// stop playback by destroying audio connection
@@ -104,20 +104,20 @@ namespace audio
104104
return GetDeviceError(audioDevice->Stop());
105105
}
106106

107-
audio::RetCode PlaybackOperation::Pause()
107+
RetCode PlaybackOperation::Pause()
108108
{
109109
if (state == State::Paused || state == State::Idle || outputConnection == nullptr) {
110110
return RetCode::InvokedInIncorrectState;
111111
}
112112
const auto retCode = GetDeviceError(audioDevice->Pause());
113-
if (retCode == audio::RetCode::Success) {
113+
if (retCode == RetCode::Success) {
114114
state = State::Paused;
115115
outputConnection->disable();
116116
}
117117
return retCode;
118118
}
119119

120-
audio::RetCode PlaybackOperation::Resume()
120+
RetCode PlaybackOperation::Resume()
121121
{
122122
if (state == State::Active || state == State::Idle) {
123123
return RetCode::InvokedInIncorrectState;
@@ -132,14 +132,14 @@ namespace audio
132132
return GetDeviceError(audioDevice->Resume());
133133
}
134134

135-
audio::RetCode PlaybackOperation::SetOutputVolume(float vol)
135+
RetCode PlaybackOperation::SetOutputVolume(float vol)
136136
{
137137
currentProfile->SetOutputVolume(vol);
138138
auto ret = audioDevice->setOutputVolume(vol);
139139
return GetDeviceError(ret);
140140
}
141141

142-
audio::RetCode PlaybackOperation::SetInputGain(float gain)
142+
RetCode PlaybackOperation::SetInputGain(float gain)
143143
{
144144
currentProfile->SetInputGain(gain);
145145
auto ret = audioDevice->setInputGain(gain);
@@ -151,22 +151,21 @@ namespace audio
151151
return dec->getCurrentPosition();
152152
}
153153

154-
audio::RetCode PlaybackOperation::SwitchToPriorityProfile(audio::PlaybackType playbackType)
154+
RetCode PlaybackOperation::SwitchToPriorityProfile(PlaybackType playbackType)
155155
{
156156
for (const auto &p : supportedProfiles) {
157157
const auto profileType = p.profile->GetType();
158-
if (profileType == audio::Profile::Type::PlaybackBluetoothA2DP &&
159-
playbackType == audio::PlaybackType::CallRingtone) {
158+
if (profileType == Profile::Type::PlaybackBluetoothA2DP && playbackType == PlaybackType::CallRingtone) {
160159
continue;
161160
}
162161
if (p.isAvailable) {
163162
return SwitchProfile(profileType);
164163
}
165164
}
166-
return audio::RetCode::ProfileNotSet;
165+
return RetCode::ProfileNotSet;
167166
}
168167

169-
audio::RetCode PlaybackOperation::SendEvent(std::shared_ptr<Event> evt)
168+
RetCode PlaybackOperation::SendEvent(std::shared_ptr<Event> evt)
170169
{
171170
const auto isAvailable = evt->getDeviceState() == Event::DeviceState::Connected;
172171
switch (evt->getType()) {
@@ -185,7 +184,7 @@ namespace audio
185184
return RetCode::Success;
186185
}
187186

188-
audio::RetCode PlaybackOperation::SwitchProfile(const Profile::Type type)
187+
RetCode PlaybackOperation::SwitchProfile(const Profile::Type type)
189188
{
190189
auto newProfile = GetProfile(type);
191190
if (newProfile == nullptr) {
@@ -199,7 +198,7 @@ namespace audio
199198

200199
// adjust new profile with information from file's tags
201200
newProfile->SetSampleRate(dec->getSourceFormat().getSampleRate());
202-
newProfile->SetInOutFlags(static_cast<std::uint32_t>(audio::codec::Flags::OutputStereo));
201+
newProfile->SetInOutFlags(static_cast<std::uint32_t>(codec::Flags::OutputStereo));
203202

204203
/// profile change - (re)create output device; stop audio first by
205204
/// killing audio connection
@@ -228,7 +227,7 @@ namespace audio
228227
Start(operationToken);
229228
}
230229

231-
return audio::RetCode::Success;
230+
return RetCode::Success;
232231
}
233232

234233
PlaybackOperation::~PlaybackOperation()

module-audio/Audio/Operation/PlaybackOperation.hpp

+13-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
1+
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
22
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md
33

44
#pragma once
@@ -19,28 +19,27 @@ namespace audio
1919
{
2020
public:
2121
PlaybackOperation(const std::string &filePath,
22-
const audio::PlaybackType &playbackType,
23-
const audio::PlaybackMode &playbackMode,
22+
const PlaybackType &playbackType,
23+
const PlaybackMode &playbackMode,
2424
AudioServiceMessage::Callback callback = nullptr);
2525

2626
~PlaybackOperation() override;
2727

28-
audio::RetCode Start(audio::Token token) final;
29-
audio::RetCode Stop() final;
30-
audio::RetCode Pause() final;
31-
audio::RetCode Resume() final;
32-
audio::RetCode SendEvent(std::shared_ptr<Event> evt) final;
33-
audio::RetCode SwitchProfile(const Profile::Type type) final;
34-
audio::RetCode SetOutputVolume(float vol) final;
35-
audio::RetCode SetInputGain(float gain) final;
28+
RetCode Start(Token token) final;
29+
RetCode Stop() final;
30+
RetCode Pause() final;
31+
RetCode Resume() final;
32+
RetCode SendEvent(std::shared_ptr<Event> evt) final;
33+
RetCode SwitchProfile(const Profile::Type type) final;
34+
RetCode SetOutputVolume(float vol) final;
35+
RetCode SetInputGain(float gain) final;
3636

3737
Position GetPosition() final;
38-
audio::RetCode SwitchToPriorityProfile(audio::PlaybackType playbackType) final;
38+
RetCode SwitchToPriorityProfile(PlaybackType playbackType) final;
3939

4040
private:
4141
static constexpr auto playbackTimeConstraint = 10ms;
42-
static constexpr auto playbackStartPosition = 0U;
43-
audio::PlaybackMode playbackMode = audio::PlaybackMode::Single;
42+
PlaybackMode playbackMode = PlaybackMode::Single;
4443

4544
std::unique_ptr<Stream> dataStreamOut;
4645
std::unique_ptr<Decoder> dec;

module-audio/Audio/decoder/Decoder.cpp

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
1+
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
22
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md
33

44
#include <cstdio>
@@ -42,12 +42,12 @@ namespace audio
4242
}
4343
}
4444

45-
std::unique_ptr<tags::fetcher::Tags> Decoder::fetchTags()
45+
auto Decoder::fetchTags() -> std::unique_ptr<tags::fetcher::Tags>
4646
{
4747
return std::make_unique<tags::fetcher::Tags>(tags::fetcher::fetchTags(filePath));
4848
}
4949

50-
std::unique_ptr<Decoder> Decoder::Create(const std::string &filePath)
50+
auto Decoder::Create(const std::string &filePath) -> std::unique_ptr<Decoder>
5151
{
5252
const auto extension = std::filesystem::path(filePath).extension();
5353
const auto extensionLowercase = utils::stringToLowercase(extension);
@@ -74,10 +74,11 @@ namespace audio
7474
return dec;
7575
}
7676

77-
void Decoder::startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback,
78-
const DecoderWorker::FileDeletedCallback &fileDeletedCallback)
77+
auto Decoder::startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback,
78+
const DecoderWorker::FileDeletedCallback &fileDeletedCallback) -> void
7979
{
8080
assert(_stream != nullptr);
81+
8182
if (audioWorker == nullptr) {
8283
const auto channelMode = (tags->num_channel == 1) ? DecoderWorker::ChannelMode::ForceStereo
8384
: DecoderWorker::ChannelMode::NoConversion;
@@ -92,25 +93,25 @@ namespace audio
9293
}
9394
}
9495

95-
void Decoder::stopDecodingWorker()
96+
auto Decoder::stopDecodingWorker() -> void
9697
{
9798
if (audioWorker) {
9899
audioWorker->close();
99100
}
100101
audioWorker = nullptr;
101102
}
102103

103-
void Decoder::onDataReceive()
104+
auto Decoder::onDataReceive() -> void
104105
{
105106
audioWorker->enablePlayback();
106107
}
107108

108-
void Decoder::enableInput()
109+
auto Decoder::enableInput() -> void
109110
{
110111
audioWorker->enablePlayback();
111112
}
112113

113-
void Decoder::disableInput()
114+
auto Decoder::disableInput() -> void
114115
{
115116
audioWorker->disablePlayback();
116117
}

module-audio/Audio/decoder/Decoder.hpp

+18-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
1+
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
22
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md
33

44
#pragma once
@@ -28,49 +28,52 @@ namespace audio
2828
explicit Decoder(const std::string &path);
2929
virtual ~Decoder();
3030

31-
virtual std::int32_t decode(std::uint32_t samplesToRead, std::int16_t *pcmData) = 0;
31+
virtual auto decode(std::uint32_t samplesToRead, std::int16_t *pcmData) -> std::int32_t = 0;
3232

3333
// Range 0 - 1
34-
virtual void setPosition(float pos) = 0;
34+
virtual auto setPosition(float pos) -> void = 0;
3535

36-
std::uint32_t getSampleRate()
36+
// Rewind to first audio sample
37+
virtual auto rewind() -> void = 0;
38+
39+
[[nodiscard]] auto getSampleRate() const noexcept -> std::uint32_t
3740
{
3841
return sampleRate;
3942
}
4043

41-
std::uint32_t getChannelCount()
44+
[[nodiscard]] auto getChannelCount() const noexcept -> std::uint32_t
4245
{
4346
return channelCount;
4447
}
4548

46-
float getCurrentPosition()
49+
[[nodiscard]] auto getCurrentPosition() const noexcept -> float
4750
{
4851
return position;
4952
}
5053

51-
void onDataReceive() override;
52-
void enableInput() override;
53-
void disableInput() override;
54+
auto onDataReceive() -> void override;
55+
auto enableInput() -> void override;
56+
auto disableInput() -> void override;
5457

5558
auto getSourceFormat() -> AudioFormat override;
5659
auto getSupportedFormats() -> std::vector<AudioFormat> override;
5760

5861
auto getTraits() const -> Endpoint::Traits override;
5962

60-
void startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback,
61-
const DecoderWorker::FileDeletedCallback &fileDeletedCallback);
62-
void stopDecodingWorker();
63+
auto startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback,
64+
const DecoderWorker::FileDeletedCallback &fileDeletedCallback) -> void;
65+
auto stopDecodingWorker() -> void;
6366

6467
// Factory method
65-
static std::unique_ptr<Decoder> Create(const std::string &path);
68+
static auto Create(const std::string &path) -> std::unique_ptr<Decoder>;
6669

6770
protected:
68-
virtual auto getBitWidth() -> unsigned int
71+
[[nodiscard]] virtual auto getBitWidth() -> unsigned int
6972
{
7073
return bitsPerSample;
7174
}
7275

73-
virtual std::unique_ptr<tags::fetcher::Tags> fetchTags();
76+
virtual auto fetchTags() -> std::unique_ptr<tags::fetcher::Tags>;
7477

7578
static constexpr Endpoint::Traits decoderCaps = {.usesDMA = false};
7679

0 commit comments

Comments
 (0)