react-native-audio-api 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +8 -3
  2. package/RNAudioAPI.podspec +0 -2
  3. package/android/src/main/cpp/audioapi/CMakeLists.txt +8 -2
  4. package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +7 -29
  5. package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.cpp +83 -0
  6. package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.h +14 -0
  7. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +4 -3
  8. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +1 -0
  9. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +37 -21
  10. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +1 -1
  11. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +21 -0
  12. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +26 -4
  13. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +1 -0
  14. package/common/cpp/audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.cpp +2 -2
  15. package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.cpp +3 -3
  16. package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.cpp +60 -0
  17. package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.h +24 -0
  18. package/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +2 -2
  19. package/common/cpp/audioapi/core/OfflineAudioContext.cpp +0 -1
  20. package/common/cpp/audioapi/core/OfflineAudioContext.h +0 -1
  21. package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +4 -10
  22. package/common/cpp/audioapi/core/effects/ConvolverNode.h +0 -4
  23. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +13 -11
  24. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +5 -9
  25. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +47 -139
  26. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +11 -8
  27. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +29 -114
  28. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +6 -8
  29. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +9 -11
  30. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.h +1 -1
  31. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +2 -2
  32. package/common/cpp/audioapi/core/types/AudioFormat.h +3 -1
  33. package/common/cpp/audioapi/core/utils/AudioDecoder.cpp +120 -91
  34. package/common/cpp/audioapi/core/utils/AudioDecoder.h +24 -101
  35. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.cpp +862 -0
  36. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.h +164 -0
  37. package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +2 -4
  38. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +7 -12
  39. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +2 -0
  40. package/common/cpp/audioapi/core/utils/buffer/BufferProcessingDirection.h +6 -0
  41. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.cpp +110 -0
  42. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.h +75 -0
  43. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.cpp +129 -0
  44. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.h +55 -0
  45. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.cpp +95 -0
  46. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.h +52 -0
  47. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +65 -157
  48. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +52 -33
  49. package/common/cpp/audioapi/events/AudioEventPayload.h +87 -0
  50. package/common/cpp/audioapi/events/IAudioEventHandlerRegistry.h +12 -12
  51. package/common/cpp/audioapi/libs/decoding/IncrementalAudioDecoder.h +12 -10
  52. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +152 -78
  53. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +6 -6
  54. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.cpp +34 -20
  55. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.h +4 -4
  56. package/common/cpp/audioapi/utils/AudioArray.hpp +6 -1
  57. package/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +1 -1
  58. package/common/cpp/audioapi/utils/TaskOffloader.hpp +3 -5
  59. package/ios/audioapi/ios/AudioAPIModule.h +2 -1
  60. package/ios/audioapi/ios/AudioAPIModule.mm +4 -25
  61. package/ios/audioapi/ios/core/IOSAudioPlayer.h +16 -2
  62. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +90 -24
  63. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
  64. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +64 -20
  65. package/ios/audioapi/ios/system/AudioSessionManager.mm +18 -7
  66. package/ios/audioapi/ios/system/SystemNotificationManager.mm +22 -22
  67. package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +10 -13
  68. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +1 -1
  69. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
  70. package/lib/commonjs/api.js +8 -0
  71. package/lib/commonjs/api.js.map +1 -1
  72. package/lib/commonjs/api.web.js +5 -0
  73. package/lib/commonjs/api.web.js.map +1 -1
  74. package/lib/commonjs/core/AudioFileUtils.js +35 -0
  75. package/lib/commonjs/core/AudioFileUtils.js.map +1 -0
  76. package/lib/commonjs/mock/index.js +15 -1
  77. package/lib/commonjs/mock/index.js.map +1 -1
  78. package/lib/module/AudioAPIModule/AudioAPIModule.js +1 -1
  79. package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
  80. package/lib/module/api.js +1 -0
  81. package/lib/module/api.js.map +1 -1
  82. package/lib/module/api.web.js +3 -0
  83. package/lib/module/api.web.js.map +1 -1
  84. package/lib/module/core/AudioFileUtils.js +31 -0
  85. package/lib/module/core/AudioFileUtils.js.map +1 -0
  86. package/lib/module/mock/index.js +14 -1
  87. package/lib/module/mock/index.js.map +1 -1
  88. package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
  89. package/lib/typescript/api.d.ts +1 -0
  90. package/lib/typescript/api.d.ts.map +1 -1
  91. package/lib/typescript/api.web.d.ts +1 -0
  92. package/lib/typescript/api.web.d.ts.map +1 -1
  93. package/lib/typescript/core/AudioFileUtils.d.ts +2 -0
  94. package/lib/typescript/core/AudioFileUtils.d.ts.map +1 -0
  95. package/lib/typescript/interfaces.d.ts +3 -0
  96. package/lib/typescript/interfaces.d.ts.map +1 -1
  97. package/lib/typescript/mock/index.d.ts +3 -1
  98. package/lib/typescript/mock/index.d.ts.map +1 -1
  99. package/package.json +3 -3
  100. package/scripts/download-prebuilt-binaries.sh +34 -1
  101. package/src/AudioAPIModule/AudioAPIModule.ts +1 -0
  102. package/src/AudioAPIModule/globals.d.ts +3 -0
  103. package/src/api.ts +1 -0
  104. package/src/api.web.ts +7 -0
  105. package/src/core/AudioFileUtils.ts +49 -0
  106. package/src/interfaces.ts +7 -0
  107. package/src/mock/index.ts +29 -0
@@ -1,34 +1,34 @@
1
1
  #pragma once
2
2
 
3
- #include <ReactCommon/CallInvoker.h>
4
3
  #include <audioapi/events/AudioEvent.h>
4
+ #include <audioapi/events/AudioEventPayload.h>
5
+ #include <audioapi/utils/Macros.h>
5
6
  #include <jsi/jsi.h>
7
+ #include <cstdint>
6
8
  #include <memory>
7
- #include <string>
8
- #include <unordered_map>
9
- #include <variant>
10
9
 
11
10
  namespace audioapi {
12
11
 
13
- using EventValue =
14
- std::variant<int, float, double, std::string, bool, std::shared_ptr<facebook::jsi::HostObject>>;
15
-
16
12
  class IAudioEventHandlerRegistry {
17
13
  public:
14
+ IAudioEventHandlerRegistry() = default;
18
15
  virtual ~IAudioEventHandlerRegistry() = default;
19
16
 
17
+ DELETE_COPY_AND_MOVE(IAudioEventHandlerRegistry);
18
+
20
19
  virtual uint64_t registerHandler(
21
20
  AudioEvent eventName,
22
21
  const std::shared_ptr<facebook::jsi::Function> &handler) = 0;
22
+
23
23
  virtual void unregisterHandler(AudioEvent eventName, uint64_t listenerId) = 0;
24
24
 
25
- virtual void invokeHandlerWithEventBody(
26
- AudioEvent eventName,
27
- const std::unordered_map<std::string, EventValue> &body) = 0;
28
- virtual void invokeHandlerWithEventBody(
25
+ /// @brief Enqueue an event for dispatch to JS handlers. Lock-free, allocation-free.
26
+ /// @param listenerId Target handler. Pass kBroadcastListenerId (0) to invoke all handlers.
27
+ /// @return true if enqueued; false if the internal channel is full (event dropped).
28
+ virtual bool dispatchEvent(
29
29
  AudioEvent eventName,
30
30
  uint64_t listenerId,
31
- const std::unordered_map<std::string, EventValue> &body) = 0;
31
+ AudioEventPayload &&payload) noexcept = 0;
32
32
  };
33
33
 
34
34
  } // namespace audioapi
@@ -1,25 +1,27 @@
1
1
  #pragma once
2
2
  #include <cstddef>
3
3
  #include <string>
4
+ #include <audioapi/utils/Result.hpp>
4
5
  #include <audioapi/utils/Macros.h>
5
6
 
6
7
  namespace audioapi::decoding {
8
+ using DecoderResult = Result<NoneType, std::string>;
7
9
  /**
8
10
  * Incremental PCM decoder: openFile or openMemory → readPcmFrames in a loop → close.
9
11
  * Shared contract for FFmpeg-based and MiniAudio-based implementations.
10
12
  */
11
- class IIncrementalAudioDecoder {
13
+ class IncrementalAudioDecoder {
12
14
  public:
13
15
  static constexpr size_t CHUNK_SIZE = 4096;
14
- IIncrementalAudioDecoder() = default;
15
- virtual ~IIncrementalAudioDecoder() = default;
16
- DELETE_COPY_AND_MOVE(IIncrementalAudioDecoder);
16
+ IncrementalAudioDecoder() = default;
17
+ virtual ~IncrementalAudioDecoder() = default;
18
+ DELETE_COPY_AND_MOVE(IncrementalAudioDecoder);
17
19
 
18
20
  /// @brief Opens a file for decoding.
19
21
  /// @param outputSampleRate The output sample rate.
20
22
  /// @param path The path to the file.
21
- /// @return True if the file was opened successfully, false otherwise.
22
- [[nodiscard]] virtual bool openFile(
23
+ /// @return Ok(None) on success or Err(message) on failure.
24
+ [[nodiscard]] virtual DecoderResult openFile(
23
25
  int outputSampleRate,
24
26
  const std::string &path) = 0;
25
27
 
@@ -27,8 +29,8 @@ class IIncrementalAudioDecoder {
27
29
  /// @param outputSampleRate The output sample rate.
28
30
  /// @param data The data to decode.
29
31
  /// @param size The size of the data.
30
- /// @return True if the memory block was opened successfully, false otherwise.
31
- [[nodiscard]] virtual bool openMemory(
32
+ /// @return Ok(None) on success or Err(message) on failure.
33
+ [[nodiscard]] virtual DecoderResult openMemory(
32
34
  int outputSampleRate,
33
35
  const void *data,
34
36
  size_t size) = 0;
@@ -64,7 +66,7 @@ class IIncrementalAudioDecoder {
64
66
 
65
67
  /// @brief Seeks to a specific time in the audio.
66
68
  /// @param seconds The time to seek to in seconds.
67
- /// @return True if the seek was successful, false otherwise.
68
- [[nodiscard]] virtual bool seekToTime(double seconds) = 0;
69
+ /// @return Ok(None) on success or Err(message) on failure.
70
+ [[nodiscard]] virtual DecoderResult seekToTime(double seconds) = 0;
69
71
  };
70
72
  } // namespace audioapi::decoding
@@ -12,18 +12,32 @@
12
12
  #if !RN_AUDIO_API_FFMPEG_DISABLED
13
13
 
14
14
  #include <algorithm>
15
+ #include <array>
15
16
  #include <cmath>
16
17
  #include <cstring>
17
18
 
18
19
  extern "C" {
19
20
  #include <libavutil/avutil.h>
20
21
  #include <libavutil/channel_layout.h>
22
+ #include <libavutil/error.h>
21
23
  #include <libavutil/mathematics.h>
22
24
  #include <libavutil/rational.h>
23
25
  }
24
26
 
25
27
  namespace audioapi::ffmpegdecoder {
26
28
 
29
+ namespace {
30
+
31
+ std::string parseFFmpegError(int errorCode) {
32
+ std::array<char, AV_ERROR_MAX_STRING_SIZE> errorBuffer{};
33
+ if (av_strerror(errorCode, errorBuffer.data(), errorBuffer.size()) == 0) {
34
+ return std::string(errorBuffer.data()) + " (" + std::to_string(errorCode) + ")";
35
+ }
36
+ return "Unknown FFmpeg error (" + std::to_string(errorCode) + ")";
37
+ }
38
+
39
+ } // namespace
40
+
27
41
  int read_packet(void *opaque, uint8_t *buf, int buf_size) {
28
42
  auto *ctx = static_cast<MemoryIOContext *>(opaque);
29
43
  if (ctx->pos >= ctx->size) {
@@ -65,30 +79,37 @@ int findAudioStreamIndex(AVFormatContext *fmt_ctx) {
65
79
  return -1;
66
80
  }
67
81
 
68
- bool openCodec(AVFormatContext *fmt_ctx, int &audio_stream_index, AVCodecContext **out_codec) {
82
+ decoding::DecoderResult openCodec(
83
+ AVFormatContext *fmt_ctx,
84
+ int &audio_stream_index,
85
+ AVCodecContext **out_codec) {
69
86
  audio_stream_index = findAudioStreamIndex(fmt_ctx);
70
87
  if (audio_stream_index < 0) {
71
- return false;
88
+ return Err("FFmpegDecoder::openCodec failed: no audio stream found");
72
89
  }
73
90
  AVCodecParameters *codecpar = fmt_ctx->streams[audio_stream_index]->codecpar;
74
91
  const AVCodec *codec = avcodec_find_decoder(codecpar->codec_id);
75
92
  if (codec == nullptr) {
76
- return false;
93
+ return Err("FFmpegDecoder::openCodec failed: decoder not found");
77
94
  }
78
95
  AVCodecContext *ctx = avcodec_alloc_context3(codec);
79
96
  if (ctx == nullptr) {
80
- return false;
97
+ return Err("FFmpegDecoder::openCodec failed: avcodec_alloc_context3 returned null");
81
98
  }
82
- if (avcodec_parameters_to_context(ctx, codecpar) < 0) {
99
+ const int parametersResult = avcodec_parameters_to_context(ctx, codecpar);
100
+ if (parametersResult < 0) {
83
101
  avcodec_free_context(&ctx);
84
- return false;
102
+ return Err(
103
+ "FFmpegDecoder::openCodec failed: avcodec_parameters_to_context failed: " +
104
+ parseFFmpegError(parametersResult));
85
105
  }
86
- if (avcodec_open2(ctx, codec, nullptr) < 0) {
106
+ const int openResult = avcodec_open2(ctx, codec, nullptr);
107
+ if (openResult < 0) {
87
108
  avcodec_free_context(&ctx);
88
- return false;
109
+ return Err("FFmpegDecoder::openCodec failed: avcodec_open2 failed: " + parseFFmpegError(openResult));
89
110
  }
90
111
  *out_codec = ctx;
91
- return true;
112
+ return Ok(None);
92
113
  }
93
114
 
94
115
  FFmpegDecoder::~FFmpegDecoder() {
@@ -128,10 +149,10 @@ void FFmpegDecoder::close() {
128
149
  total_output_frames_ = 0;
129
150
  }
130
151
 
131
- bool FFmpegDecoder::setupSwr() {
152
+ decoding::DecoderResult FFmpegDecoder::setupSwr() {
132
153
  swr_ = swr_alloc();
133
154
  if (swr_ == nullptr) {
134
- return false;
155
+ return Err("FFmpegDecoder::setupSwr failed: swr_alloc returned null");
135
156
  }
136
157
  av_opt_set_chlayout(swr_, "in_chlayout", &codec_ctx_->ch_layout, 0);
137
158
  av_opt_set_int(swr_, "in_sample_rate", codec_ctx_->sample_rate, 0);
@@ -142,43 +163,55 @@ bool FFmpegDecoder::setupSwr() {
142
163
  av_opt_set_chlayout(swr_, "out_chlayout", &out_layout, 0);
143
164
  av_opt_set_int(swr_, "out_sample_rate", output_sample_rate_, 0);
144
165
  av_opt_set_sample_fmt(swr_, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
145
- if (swr_init(swr_) < 0) {
166
+ const int swrInitResult = swr_init(swr_);
167
+ if (swrInitResult < 0) {
146
168
  av_channel_layout_uninit(&out_layout);
147
- return false;
169
+ return Err(
170
+ "FFmpegDecoder::setupSwr failed: swr_init failed: " + parseFFmpegError(swrInitResult));
148
171
  }
149
172
  av_channel_layout_uninit(&out_layout);
150
173
 
151
- if (av_samples_alloc_array_and_samples(
152
- &resampled_data_,
153
- nullptr,
154
- output_channels_,
155
- decoding::IIncrementalAudioDecoder::CHUNK_SIZE,
156
- AV_SAMPLE_FMT_FLT,
157
- 0) < 0) {
158
- return false;
159
- }
160
- max_resampled_samples_ = static_cast<int>(decoding::IIncrementalAudioDecoder::CHUNK_SIZE);
161
- return true;
174
+ const int allocResult = av_samples_alloc_array_and_samples(
175
+ &resampled_data_,
176
+ nullptr,
177
+ output_channels_,
178
+ decoding::IncrementalAudioDecoder::CHUNK_SIZE,
179
+ AV_SAMPLE_FMT_FLT,
180
+ 0);
181
+ if (allocResult < 0) {
182
+ return Err(
183
+ "FFmpegDecoder::setupSwr failed: av_samples_alloc_array_and_samples failed: " +
184
+ parseFFmpegError(allocResult));
185
+ }
186
+ max_resampled_samples_ = static_cast<int>(decoding::IncrementalAudioDecoder::CHUNK_SIZE);
187
+ return Ok(None);
162
188
  }
163
189
 
164
- bool FFmpegDecoder::openFile(int outputSampleRate, const std::string &path) {
190
+ decoding::DecoderResult FFmpegDecoder::openFile(int outputSampleRate, const std::string &path) {
165
191
  close();
166
192
  if (path.empty()) {
167
- return false;
193
+ return Err("FFmpegDecoder::openFile failed: path is empty");
168
194
  }
169
- if (avformat_open_input(&fmt_ctx_, path.c_str(), nullptr, nullptr) < 0) {
195
+ const int openInputResult = avformat_open_input(&fmt_ctx_, path.c_str(), nullptr, nullptr);
196
+ if (openInputResult < 0) {
170
197
  fmt_ctx_ = nullptr;
171
- return false;
198
+ return Err(
199
+ "FFmpegDecoder::openFile failed: avformat_open_input failed: " +
200
+ parseFFmpegError(openInputResult));
172
201
  }
173
- if (avformat_find_stream_info(fmt_ctx_, nullptr) < 0) {
202
+ const int streamInfoResult = avformat_find_stream_info(fmt_ctx_, nullptr);
203
+ if (streamInfoResult < 0) {
174
204
  avformat_close_input(&fmt_ctx_);
175
205
  fmt_ctx_ = nullptr;
176
- return false;
206
+ return Err(
207
+ "FFmpegDecoder::openFile failed: avformat_find_stream_info failed: " +
208
+ parseFFmpegError(streamInfoResult));
177
209
  }
178
- if (!openCodec(fmt_ctx_, audio_stream_index_, &codec_ctx_)) {
210
+ auto codecResult = openCodec(fmt_ctx_, audio_stream_index_, &codec_ctx_);
211
+ if (codecResult.is_err()) {
179
212
  avformat_close_input(&fmt_ctx_);
180
213
  fmt_ctx_ = nullptr;
181
- return false;
214
+ return codecResult;
182
215
  }
183
216
  output_channels_ = codec_ctx_->ch_layout.nb_channels;
184
217
  output_sample_rate_ =
@@ -186,21 +219,30 @@ bool FFmpegDecoder::openFile(int outputSampleRate, const std::string &path) {
186
219
 
187
220
  packet_ = av_packet_alloc();
188
221
  frame_ = av_frame_alloc();
189
- if (packet_ == nullptr || frame_ == nullptr || !setupSwr()) {
222
+ if (packet_ == nullptr) {
223
+ close();
224
+ return Err("FFmpegDecoder::openFile failed: av_packet_alloc returned null");
225
+ }
226
+ if (frame_ == nullptr) {
190
227
  close();
191
- return false;
228
+ return Err("FFmpegDecoder::openFile failed: av_frame_alloc returned null");
229
+ }
230
+ auto swrResult = setupSwr();
231
+ if (swrResult.is_err()) {
232
+ close();
233
+ return swrResult;
192
234
  }
193
235
  total_output_frames_ = 0;
194
- return true;
236
+ return Ok(None);
195
237
  }
196
238
 
197
- bool FFmpegDecoder::openMemory(
239
+ decoding::DecoderResult FFmpegDecoder::openMemory(
198
240
  int outputSampleRate,
199
241
  const void *data,
200
242
  size_t size) {
201
243
  close();
202
244
  if (data == nullptr || size == 0) {
203
- return false;
245
+ return Err("FFmpegDecoder::openMemory failed: input data is empty");
204
246
  }
205
247
  mem_io_ = std::make_unique<MemoryIOContext>();
206
248
  mem_io_->data = static_cast<const uint8_t *>(data);
@@ -208,14 +250,14 @@ bool FFmpegDecoder::openMemory(
208
250
  mem_io_->pos = 0;
209
251
 
210
252
  auto* io_buf =
211
- static_cast<uint8_t *>(av_malloc(decoding::IIncrementalAudioDecoder::CHUNK_SIZE));
253
+ static_cast<uint8_t *>(av_malloc(decoding::IncrementalAudioDecoder::CHUNK_SIZE));
212
254
  if (io_buf == nullptr) {
213
255
  close();
214
- return false;
256
+ return Err("FFmpegDecoder::openMemory failed: av_malloc returned null");
215
257
  }
216
258
  avio_ctx_ = avio_alloc_context(
217
259
  io_buf,
218
- static_cast<int>(decoding::IIncrementalAudioDecoder::CHUNK_SIZE),
260
+ static_cast<int>(decoding::IncrementalAudioDecoder::CHUNK_SIZE),
219
261
  0,
220
262
  mem_io_.get(),
221
263
  read_packet,
@@ -224,27 +266,34 @@ bool FFmpegDecoder::openMemory(
224
266
  if (avio_ctx_ == nullptr) {
225
267
  av_free(io_buf);
226
268
  mem_io_.reset();
227
- return false;
269
+ return Err("FFmpegDecoder::openMemory failed: avio_alloc_context returned null");
228
270
  }
229
271
 
230
272
  fmt_ctx_ = avformat_alloc_context();
231
273
  if (fmt_ctx_ == nullptr) {
232
274
  close();
233
- return false;
275
+ return Err("FFmpegDecoder::openMemory failed: avformat_alloc_context returned null");
234
276
  }
235
277
  fmt_ctx_->pb = avio_ctx_;
236
278
 
237
- if (avformat_open_input(&fmt_ctx_, nullptr, nullptr, nullptr) < 0) {
279
+ const int openInputResult = avformat_open_input(&fmt_ctx_, nullptr, nullptr, nullptr);
280
+ if (openInputResult < 0) {
238
281
  close();
239
- return false;
282
+ return Err(
283
+ "FFmpegDecoder::openMemory failed: avformat_open_input failed: " +
284
+ parseFFmpegError(openInputResult));
240
285
  }
241
- if (avformat_find_stream_info(fmt_ctx_, nullptr) < 0) {
286
+ const int streamInfoResult = avformat_find_stream_info(fmt_ctx_, nullptr);
287
+ if (streamInfoResult < 0) {
242
288
  close();
243
- return false;
289
+ return Err(
290
+ "FFmpegDecoder::openMemory failed: avformat_find_stream_info failed: " +
291
+ parseFFmpegError(streamInfoResult));
244
292
  }
245
- if (!openCodec(fmt_ctx_, audio_stream_index_, &codec_ctx_)) {
293
+ auto codecResult = openCodec(fmt_ctx_, audio_stream_index_, &codec_ctx_);
294
+ if (codecResult.is_err()) {
246
295
  close();
247
- return false;
296
+ return codecResult;
248
297
  }
249
298
  output_channels_ = codec_ctx_->ch_layout.nb_channels;
250
299
  output_sample_rate_ =
@@ -252,12 +301,21 @@ bool FFmpegDecoder::openMemory(
252
301
 
253
302
  packet_ = av_packet_alloc();
254
303
  frame_ = av_frame_alloc();
255
- if (packet_ == nullptr || frame_ == nullptr || !setupSwr()) {
304
+ if (packet_ == nullptr) {
305
+ close();
306
+ return Err("FFmpegDecoder::openMemory failed: av_packet_alloc returned null");
307
+ }
308
+ if (frame_ == nullptr) {
256
309
  close();
257
- return false;
310
+ return Err("FFmpegDecoder::openMemory failed: av_frame_alloc returned null");
311
+ }
312
+ auto swrResult = setupSwr();
313
+ if (swrResult.is_err()) {
314
+ close();
315
+ return swrResult;
258
316
  }
259
317
  total_output_frames_ = 0;
260
- return true;
318
+ return Ok(None);
261
319
  }
262
320
 
263
321
  void FFmpegDecoder::appendFrameResampled(AVFrame *frame) {
@@ -289,29 +347,38 @@ void FFmpegDecoder::appendFrameResampled(AVFrame *frame) {
289
347
  }
290
348
  }
291
349
 
292
- bool FFmpegDecoder::feedPipeline() {
350
+ decoding::DecoderResult FFmpegDecoder::feedPipeline() {
293
351
  for (;;) {
294
352
  int r = avcodec_receive_frame(codec_ctx_, frame_);
295
353
  if (r == 0) {
296
354
  appendFrameResampled(frame_);
297
- return true;
355
+ return Ok(None);
298
356
  }
299
357
  if (r == AVERROR_EOF) {
300
- return !leftover_.empty();
358
+ if (leftover_.empty()) {
359
+ return Err("FFmpegDecoder::feedPipeline reached end of stream");
360
+ }
361
+ return Ok(None);
301
362
  }
302
363
  if (r != AVERROR(EAGAIN)) {
303
- return false;
364
+ return Err(
365
+ "FFmpegDecoder::feedPipeline failed: avcodec_receive_frame failed: " +
366
+ parseFFmpegError(r));
304
367
  }
305
368
 
306
369
  r = av_read_frame(fmt_ctx_, packet_);
307
370
  if (r == AVERROR_EOF) {
308
- if (avcodec_send_packet(codec_ctx_, nullptr) < 0) {
309
- return false;
371
+ const int flushResult = avcodec_send_packet(codec_ctx_, nullptr);
372
+ if (flushResult < 0) {
373
+ return Err(
374
+ "FFmpegDecoder::feedPipeline failed: avcodec_send_packet flush failed: " +
375
+ parseFFmpegError(flushResult));
310
376
  }
311
377
  continue;
312
378
  }
313
379
  if (r < 0) {
314
- return false;
380
+ return Err(
381
+ "FFmpegDecoder::feedPipeline failed: av_read_frame failed: " + parseFFmpegError(r));
315
382
  }
316
383
  if (packet_->stream_index != audio_stream_index_) {
317
384
  av_packet_unref(packet_);
@@ -320,7 +387,9 @@ bool FFmpegDecoder::feedPipeline() {
320
387
  r = avcodec_send_packet(codec_ctx_, packet_);
321
388
  av_packet_unref(packet_);
322
389
  if (r < 0) {
323
- return false;
390
+ return Err(
391
+ "FFmpegDecoder::feedPipeline failed: avcodec_send_packet failed: " +
392
+ parseFFmpegError(r));
324
393
  }
325
394
  }
326
395
  }
@@ -364,9 +433,9 @@ float FFmpegDecoder::getCurrentPositionInSeconds() const {
364
433
 
365
434
  // todo: offload this call to a separate thread because seeking decoder can take a while
366
435
  // current implementation suspends audio thread, which disable multiple playbacks
367
- bool FFmpegDecoder::seekToTime(double seconds) {
436
+ decoding::DecoderResult FFmpegDecoder::seekToTime(double seconds) {
368
437
  if (!isOpen() || audio_stream_index_ < 0 || output_sample_rate_ <= 0) {
369
- return false;
438
+ return Err("FFmpegDecoder::seekToTime failed: decoder is not open");
370
439
  }
371
440
  float dur = getDurationInSeconds();
372
441
  if (dur > 0 && std::isfinite(dur)) {
@@ -374,20 +443,23 @@ bool FFmpegDecoder::seekToTime(double seconds) {
374
443
  } else {
375
444
  seconds = std::max(0.0, seconds);
376
445
  if (!std::isfinite(seconds)) {
377
- return false;
446
+ return Err("FFmpegDecoder::seekToTime failed: seconds is not finite");
378
447
  }
379
448
  }
380
449
 
381
450
  auto ts = static_cast<int64_t>(seconds * static_cast<double>(AV_TIME_BASE));
382
- if (avformat_seek_file(fmt_ctx_, -1, INT64_MIN, ts, INT64_MAX, 0) < 0) {
383
- return false;
451
+ const int seekResult = avformat_seek_file(fmt_ctx_, -1, INT64_MIN, ts, INT64_MAX, 0);
452
+ if (seekResult < 0) {
453
+ return Err(
454
+ "FFmpegDecoder::seekToTime failed: avformat_seek_file failed: " +
455
+ parseFFmpegError(seekResult));
384
456
  }
385
457
  avcodec_flush_buffers(codec_ctx_);
386
458
  leftover_.clear();
387
459
  leftover_offset_ = 0;
388
460
  total_output_frames_ = static_cast<size_t>(
389
461
  std::llround(seconds * static_cast<double>(output_sample_rate_)));
390
- return true;
462
+ return Ok(None);
391
463
  }
392
464
 
393
465
  size_t FFmpegDecoder::readPcmFrames(float *outInterleaved, size_t frameCount) {
@@ -416,7 +488,7 @@ size_t FFmpegDecoder::readPcmFrames(float *outInterleaved, size_t frameCount) {
416
488
  leftover_offset_ = 0;
417
489
  }
418
490
  delivered += take;
419
- } else if (!feedPipeline()) {
491
+ } else if (feedPipeline().is_err()) {
420
492
  break;
421
493
  }
422
494
  }
@@ -439,15 +511,16 @@ static std::shared_ptr<AudioBuffer> buildAudioBufferFromInterleaved(
439
511
 
440
512
  std::shared_ptr<AudioBuffer> decodeWithFilePath(const std::string &path, int sample_rate) {
441
513
  FFmpegDecoder dec;
442
- if (!dec.openFile(sample_rate, path)) {
514
+ const auto openResult = dec.openFile(sample_rate, path);
515
+ if (openResult.is_err()) {
443
516
  return nullptr;
444
517
  }
445
518
  std::vector<float> acc;
446
519
  std::vector<float> tmp(
447
- decoding::IIncrementalAudioDecoder::CHUNK_SIZE *
520
+ decoding::IncrementalAudioDecoder::CHUNK_SIZE *
448
521
  static_cast<size_t>(std::max(1, dec.outputChannels())));
449
522
  while (true) {
450
- size_t n = dec.readPcmFrames(tmp.data(), decoding::IIncrementalAudioDecoder::CHUNK_SIZE);
523
+ size_t n = dec.readPcmFrames(tmp.data(), decoding::IncrementalAudioDecoder::CHUNK_SIZE);
451
524
  if (n == 0) {
452
525
  break;
453
526
  }
@@ -461,15 +534,16 @@ std::shared_ptr<AudioBuffer> decodeWithFilePath(const std::string &path, int sam
461
534
 
462
535
  std::shared_ptr<AudioBuffer> decodeWithMemoryBlock(const void *data, size_t size, int sample_rate) {
463
536
  FFmpegDecoder dec;
464
- if (!dec.openMemory(sample_rate, data, size)) {
537
+ const auto openResult = dec.openMemory(sample_rate, data, size);
538
+ if (openResult.is_err()) {
465
539
  return nullptr;
466
540
  }
467
541
  std::vector<float> acc;
468
542
  std::vector<float> tmp(
469
- decoding::IIncrementalAudioDecoder::CHUNK_SIZE *
543
+ decoding::IncrementalAudioDecoder::CHUNK_SIZE *
470
544
  static_cast<size_t>(std::max(1, dec.outputChannels())));
471
545
  while (true) {
472
- size_t n = dec.readPcmFrames(tmp.data(), decoding::IIncrementalAudioDecoder::CHUNK_SIZE);
546
+ size_t n = dec.readPcmFrames(tmp.data(), decoding::IncrementalAudioDecoder::CHUNK_SIZE);
473
547
  if (n == 0) {
474
548
  break;
475
549
  }
@@ -488,11 +562,11 @@ std::shared_ptr<AudioBuffer> decodeWithMemoryBlock(const void *data, size_t size
488
562
  namespace audioapi::ffmpegdecoder {
489
563
  FFmpegDecoder::~FFmpegDecoder() = default;
490
564
  void FFmpegDecoder::close() {}
491
- bool FFmpegDecoder::openFile(int, const std::string &) {
492
- return false;
565
+ decoding::DecoderResult FFmpegDecoder::openFile(int, const std::string &) {
566
+ return Err("FFmpeg is disabled");
493
567
  }
494
- bool FFmpegDecoder::openMemory(int, const void *, size_t) {
495
- return false;
568
+ decoding::DecoderResult FFmpegDecoder::openMemory(int, const void *, size_t) {
569
+ return Err("FFmpeg is disabled");
496
570
  }
497
571
  float FFmpegDecoder::getDurationInSeconds() const {
498
572
  return 0;
@@ -500,8 +574,8 @@ float FFmpegDecoder::getDurationInSeconds() const {
500
574
  float FFmpegDecoder::getCurrentPositionInSeconds() const {
501
575
  return 0;
502
576
  }
503
- bool FFmpegDecoder::seekToTime(double) {
504
- return false;
577
+ decoding::DecoderResult FFmpegDecoder::seekToTime(double) {
578
+ return Err("FFmpeg is disabled");
505
579
  }
506
580
  size_t FFmpegDecoder::readPcmFrames(float *, size_t) {
507
581
  return 0;
@@ -39,7 +39,7 @@ struct MemoryIOContext {
39
39
  * 2) readPcmFrames repeatedly; 0 returned = end of stream
40
40
  * 3) close when done
41
41
  */
42
- class FFmpegDecoder : public decoding::IIncrementalAudioDecoder {
42
+ class FFmpegDecoder : public decoding::IncrementalAudioDecoder {
43
43
  public:
44
44
  FFmpegDecoder() = default;
45
45
  FFmpegDecoder(const FFmpegDecoder &) = delete;
@@ -48,11 +48,11 @@ class FFmpegDecoder : public decoding::IIncrementalAudioDecoder {
48
48
  FFmpegDecoder &operator=(FFmpegDecoder &&other) = delete;
49
49
  ~FFmpegDecoder() override;
50
50
 
51
- [[nodiscard]] bool openFile(
51
+ [[nodiscard]] decoding::DecoderResult openFile(
52
52
  int outputSampleRate,
53
53
  const std::string &path) override;
54
54
 
55
- [[nodiscard]] bool openMemory(
55
+ [[nodiscard]] decoding::DecoderResult openMemory(
56
56
  int outputSampleRate,
57
57
  const void *data,
58
58
  size_t size) override;
@@ -71,11 +71,11 @@ class FFmpegDecoder : public decoding::IIncrementalAudioDecoder {
71
71
 
72
72
  [[nodiscard]] float getCurrentPositionInSeconds() const override;
73
73
 
74
- [[nodiscard]] bool seekToTime(double seconds) override;
74
+ [[nodiscard]] decoding::DecoderResult seekToTime(double seconds) override;
75
75
 
76
76
  private:
77
- bool setupSwr();
78
- bool feedPipeline();
77
+ [[nodiscard]] decoding::DecoderResult setupSwr();
78
+ [[nodiscard]] decoding::DecoderResult feedPipeline();
79
79
  void appendFrameResampled(AVFrame *frame);
80
80
 
81
81
  AVFormatContext *fmt_ctx_ = nullptr;