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.
- package/README.md +8 -3
- package/RNAudioAPI.podspec +0 -2
- package/android/src/main/cpp/audioapi/CMakeLists.txt +8 -2
- package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +7 -29
- package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.cpp +83 -0
- package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.h +14 -0
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +4 -3
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +1 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +37 -21
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +1 -1
- package/common/cpp/audioapi/AudioAPIModuleInstaller.h +21 -0
- package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +26 -4
- package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +1 -0
- package/common/cpp/audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.cpp +2 -2
- package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.cpp +3 -3
- package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.cpp +60 -0
- package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.h +24 -0
- package/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +2 -2
- package/common/cpp/audioapi/core/OfflineAudioContext.cpp +0 -1
- package/common/cpp/audioapi/core/OfflineAudioContext.h +0 -1
- package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +4 -10
- package/common/cpp/audioapi/core/effects/ConvolverNode.h +0 -4
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +13 -11
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +5 -9
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +47 -139
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +11 -8
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +29 -114
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +6 -8
- package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +9 -11
- package/common/cpp/audioapi/core/sources/AudioFileSourceNode.h +1 -1
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +2 -2
- package/common/cpp/audioapi/core/types/AudioFormat.h +3 -1
- package/common/cpp/audioapi/core/utils/AudioDecoder.cpp +120 -91
- package/common/cpp/audioapi/core/utils/AudioDecoder.h +24 -101
- package/common/cpp/audioapi/core/utils/AudioFileConcatenator.cpp +862 -0
- package/common/cpp/audioapi/core/utils/AudioFileConcatenator.h +164 -0
- package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +2 -4
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +7 -12
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +2 -0
- package/common/cpp/audioapi/core/utils/buffer/BufferProcessingDirection.h +6 -0
- package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.cpp +110 -0
- package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.h +75 -0
- package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.cpp +129 -0
- package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.h +55 -0
- package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.cpp +95 -0
- package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.h +52 -0
- package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +65 -157
- package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +52 -33
- package/common/cpp/audioapi/events/AudioEventPayload.h +87 -0
- package/common/cpp/audioapi/events/IAudioEventHandlerRegistry.h +12 -12
- package/common/cpp/audioapi/libs/decoding/IncrementalAudioDecoder.h +12 -10
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +152 -78
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +6 -6
- package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.cpp +34 -20
- package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.h +4 -4
- package/common/cpp/audioapi/utils/AudioArray.hpp +6 -1
- package/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +1 -1
- package/common/cpp/audioapi/utils/TaskOffloader.hpp +3 -5
- package/ios/audioapi/ios/AudioAPIModule.h +2 -1
- package/ios/audioapi/ios/AudioAPIModule.mm +4 -25
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +16 -2
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +90 -24
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
- package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +64 -20
- package/ios/audioapi/ios/system/AudioSessionManager.mm +18 -7
- package/ios/audioapi/ios/system/SystemNotificationManager.mm +22 -22
- package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +10 -13
- package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +1 -1
- package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
- package/lib/commonjs/api.js +8 -0
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/api.web.js +5 -0
- package/lib/commonjs/api.web.js.map +1 -1
- package/lib/commonjs/core/AudioFileUtils.js +35 -0
- package/lib/commonjs/core/AudioFileUtils.js.map +1 -0
- package/lib/commonjs/mock/index.js +15 -1
- package/lib/commonjs/mock/index.js.map +1 -1
- package/lib/module/AudioAPIModule/AudioAPIModule.js +1 -1
- package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
- package/lib/module/api.js +1 -0
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +3 -0
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/core/AudioFileUtils.js +31 -0
- package/lib/module/core/AudioFileUtils.js.map +1 -0
- package/lib/module/mock/index.js +14 -1
- package/lib/module/mock/index.js.map +1 -1
- package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/api.d.ts +1 -0
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/api.web.d.ts +1 -0
- package/lib/typescript/api.web.d.ts.map +1 -1
- package/lib/typescript/core/AudioFileUtils.d.ts +2 -0
- package/lib/typescript/core/AudioFileUtils.d.ts.map +1 -0
- package/lib/typescript/interfaces.d.ts +3 -0
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/mock/index.d.ts +3 -1
- package/lib/typescript/mock/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/scripts/download-prebuilt-binaries.sh +34 -1
- package/src/AudioAPIModule/AudioAPIModule.ts +1 -0
- package/src/AudioAPIModule/globals.d.ts +3 -0
- package/src/api.ts +1 -0
- package/src/api.web.ts +7 -0
- package/src/core/AudioFileUtils.ts +49 -0
- package/src/interfaces.ts +7 -0
- 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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
virtual
|
|
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
|
-
|
|
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
|
|
13
|
+
class IncrementalAudioDecoder {
|
|
12
14
|
public:
|
|
13
15
|
static constexpr size_t CHUNK_SIZE = 4096;
|
|
14
|
-
|
|
15
|
-
virtual ~
|
|
16
|
-
DELETE_COPY_AND_MOVE(
|
|
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
|
|
22
|
-
[[nodiscard]] virtual
|
|
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
|
|
31
|
-
[[nodiscard]] virtual
|
|
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
|
|
68
|
-
[[nodiscard]] virtual
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
97
|
+
return Err("FFmpegDecoder::openCodec failed: avcodec_alloc_context3 returned null");
|
|
81
98
|
}
|
|
82
|
-
|
|
99
|
+
const int parametersResult = avcodec_parameters_to_context(ctx, codecpar);
|
|
100
|
+
if (parametersResult < 0) {
|
|
83
101
|
avcodec_free_context(&ctx);
|
|
84
|
-
return
|
|
102
|
+
return Err(
|
|
103
|
+
"FFmpegDecoder::openCodec failed: avcodec_parameters_to_context failed: " +
|
|
104
|
+
parseFFmpegError(parametersResult));
|
|
85
105
|
}
|
|
86
|
-
|
|
106
|
+
const int openResult = avcodec_open2(ctx, codec, nullptr);
|
|
107
|
+
if (openResult < 0) {
|
|
87
108
|
avcodec_free_context(&ctx);
|
|
88
|
-
return
|
|
109
|
+
return Err("FFmpegDecoder::openCodec failed: avcodec_open2 failed: " + parseFFmpegError(openResult));
|
|
89
110
|
}
|
|
90
111
|
*out_codec = ctx;
|
|
91
|
-
return
|
|
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
|
-
|
|
152
|
+
decoding::DecoderResult FFmpegDecoder::setupSwr() {
|
|
132
153
|
swr_ = swr_alloc();
|
|
133
154
|
if (swr_ == nullptr) {
|
|
134
|
-
return
|
|
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
|
-
|
|
166
|
+
const int swrInitResult = swr_init(swr_);
|
|
167
|
+
if (swrInitResult < 0) {
|
|
146
168
|
av_channel_layout_uninit(&out_layout);
|
|
147
|
-
return
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
190
|
+
decoding::DecoderResult FFmpegDecoder::openFile(int outputSampleRate, const std::string &path) {
|
|
165
191
|
close();
|
|
166
192
|
if (path.empty()) {
|
|
167
|
-
return
|
|
193
|
+
return Err("FFmpegDecoder::openFile failed: path is empty");
|
|
168
194
|
}
|
|
169
|
-
|
|
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
|
|
198
|
+
return Err(
|
|
199
|
+
"FFmpegDecoder::openFile failed: avformat_open_input failed: " +
|
|
200
|
+
parseFFmpegError(openInputResult));
|
|
172
201
|
}
|
|
173
|
-
|
|
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
|
|
206
|
+
return Err(
|
|
207
|
+
"FFmpegDecoder::openFile failed: avformat_find_stream_info failed: " +
|
|
208
|
+
parseFFmpegError(streamInfoResult));
|
|
177
209
|
}
|
|
178
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
236
|
+
return Ok(None);
|
|
195
237
|
}
|
|
196
238
|
|
|
197
|
-
|
|
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
|
|
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::
|
|
253
|
+
static_cast<uint8_t *>(av_malloc(decoding::IncrementalAudioDecoder::CHUNK_SIZE));
|
|
212
254
|
if (io_buf == nullptr) {
|
|
213
255
|
close();
|
|
214
|
-
return
|
|
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::
|
|
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
|
|
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
|
|
275
|
+
return Err("FFmpegDecoder::openMemory failed: avformat_alloc_context returned null");
|
|
234
276
|
}
|
|
235
277
|
fmt_ctx_->pb = avio_ctx_;
|
|
236
278
|
|
|
237
|
-
|
|
279
|
+
const int openInputResult = avformat_open_input(&fmt_ctx_, nullptr, nullptr, nullptr);
|
|
280
|
+
if (openInputResult < 0) {
|
|
238
281
|
close();
|
|
239
|
-
return
|
|
282
|
+
return Err(
|
|
283
|
+
"FFmpegDecoder::openMemory failed: avformat_open_input failed: " +
|
|
284
|
+
parseFFmpegError(openInputResult));
|
|
240
285
|
}
|
|
241
|
-
|
|
286
|
+
const int streamInfoResult = avformat_find_stream_info(fmt_ctx_, nullptr);
|
|
287
|
+
if (streamInfoResult < 0) {
|
|
242
288
|
close();
|
|
243
|
-
return
|
|
289
|
+
return Err(
|
|
290
|
+
"FFmpegDecoder::openMemory failed: avformat_find_stream_info failed: " +
|
|
291
|
+
parseFFmpegError(streamInfoResult));
|
|
244
292
|
}
|
|
245
|
-
|
|
293
|
+
auto codecResult = openCodec(fmt_ctx_, audio_stream_index_, &codec_ctx_);
|
|
294
|
+
if (codecResult.is_err()) {
|
|
246
295
|
close();
|
|
247
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
355
|
+
return Ok(None);
|
|
298
356
|
}
|
|
299
357
|
if (r == AVERROR_EOF) {
|
|
300
|
-
|
|
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
|
|
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
|
-
|
|
309
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
436
|
+
decoding::DecoderResult FFmpegDecoder::seekToTime(double seconds) {
|
|
368
437
|
if (!isOpen() || audio_stream_index_ < 0 || output_sample_rate_ <= 0) {
|
|
369
|
-
return
|
|
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
|
|
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
|
-
|
|
383
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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::
|
|
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::
|
|
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
|
-
|
|
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::
|
|
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::
|
|
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
|
-
|
|
492
|
-
return
|
|
565
|
+
decoding::DecoderResult FFmpegDecoder::openFile(int, const std::string &) {
|
|
566
|
+
return Err("FFmpeg is disabled");
|
|
493
567
|
}
|
|
494
|
-
|
|
495
|
-
return
|
|
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
|
-
|
|
504
|
-
return
|
|
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::
|
|
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]]
|
|
51
|
+
[[nodiscard]] decoding::DecoderResult openFile(
|
|
52
52
|
int outputSampleRate,
|
|
53
53
|
const std::string &path) override;
|
|
54
54
|
|
|
55
|
-
[[nodiscard]]
|
|
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]]
|
|
74
|
+
[[nodiscard]] decoding::DecoderResult seekToTime(double seconds) override;
|
|
75
75
|
|
|
76
76
|
private:
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
[[nodiscard]] decoding::DecoderResult setupSwr();
|
|
78
|
+
[[nodiscard]] decoding::DecoderResult feedPipeline();
|
|
79
79
|
void appendFrameResampled(AVFrame *frame);
|
|
80
80
|
|
|
81
81
|
AVFormatContext *fmt_ctx_ = nullptr;
|