react-native-audio-api 0.13.0-nightly-51a651d-20260701 → 0.13.0
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/android/build.gradle +1 -0
- package/android/src/main/cpp/audioapi/android/core/utils/AndroidFileWriterBackend.cpp +2 -2
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +7 -3
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp +3 -3
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +2 -0
- package/android/src/oldarch/NativeAudioAPIModuleSpec.java +4 -0
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +1 -1
- package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.cpp +1 -24
- package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.h +0 -1
- package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.cpp +55 -47
- package/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +60 -6
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +1 -1
- package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +7 -2
- package/common/cpp/audioapi/core/utils/AudioDecoding.cpp +69 -25
- package/common/cpp/audioapi/core/utils/{AudioDecoding.hpp → AudioDecoding.h} +18 -13
- package/common/cpp/audioapi/core/utils/decoding/SeekDecoderDaemon.cpp +4 -4
- package/common/cpp/audioapi/core/utils/decoding/SeekDecoderDaemon.h +3 -0
- package/common/cpp/audioapi/libs/decoding/IncrementalAudioDecoder.h +11 -0
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +72 -48
- package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +10 -1
- package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.cpp +7 -0
- package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.h +5 -0
- package/common/cpp/audioapi/types/NodeOptions.h +3 -0
- package/common/cpp/audioapi/utils/TaskOffloader.hpp +43 -11
- package/ios/audioapi/ios/AudioAPIModule.mm +9 -0
- package/ios/audioapi/ios/core/utils/IOSFileWriter.mm +6 -3
- package/lib/commonjs/Audio/Audio.js +2 -0
- package/lib/commonjs/Audio/Audio.js.map +1 -1
- package/lib/commonjs/Audio/useAudioSourceLoader.js +15 -11
- package/lib/commonjs/Audio/useAudioSourceLoader.js.map +1 -1
- package/lib/commonjs/Audio/utils.js +4 -1
- package/lib/commonjs/Audio/utils.js.map +1 -1
- package/lib/commonjs/api.js +10 -2
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/api.web.js +6 -0
- package/lib/commonjs/api.web.js.map +1 -1
- package/lib/commonjs/core/AudioDecoder.js +1 -32
- package/lib/commonjs/core/AudioDecoder.js.map +1 -1
- package/lib/commonjs/core/AudioFileUtils.js +32 -11
- package/lib/commonjs/core/AudioFileUtils.js.map +1 -1
- package/lib/commonjs/mock/index.js +5 -5
- package/lib/commonjs/mock/index.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioAPIModule.web.js +1 -0
- package/lib/commonjs/specs/NativeAudioAPIModule.web.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/utils/flags.js +18 -0
- package/lib/commonjs/utils/flags.js.map +1 -0
- package/lib/commonjs/utils/index.js +24 -0
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/commonjs/utils/paths.js +9 -0
- package/lib/commonjs/utils/paths.js.map +1 -1
- package/lib/commonjs/utils/remoteHttpSource.js +80 -0
- package/lib/commonjs/utils/remoteHttpSource.js.map +1 -0
- package/lib/commonjs/web-core/AudioDecoder.web.js +6 -2
- package/lib/commonjs/web-core/AudioDecoder.web.js.map +1 -1
- package/lib/module/Audio/Audio.js +2 -0
- package/lib/module/Audio/Audio.js.map +1 -1
- package/lib/module/Audio/useAudioSourceLoader.js +16 -12
- package/lib/module/Audio/useAudioSourceLoader.js.map +1 -1
- package/lib/module/Audio/utils.js +4 -1
- package/lib/module/Audio/utils.js.map +1 -1
- package/lib/module/api.js +3 -2
- 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/AudioDecoder.js +2 -32
- package/lib/module/core/AudioDecoder.js.map +1 -1
- package/lib/module/core/AudioFileUtils.js +31 -11
- package/lib/module/core/AudioFileUtils.js.map +1 -1
- package/lib/module/mock/index.js +4 -5
- package/lib/module/mock/index.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.web.js +1 -0
- package/lib/module/specs/NativeAudioAPIModule.web.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/flags.js +15 -0
- package/lib/module/utils/flags.js.map +1 -0
- package/lib/module/utils/index.js +17 -0
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/paths.js +8 -0
- package/lib/module/utils/paths.js.map +1 -1
- package/lib/module/utils/remoteHttpSource.js +75 -0
- package/lib/module/utils/remoteHttpSource.js.map +1 -0
- package/lib/module/web-core/AudioDecoder.web.js +6 -2
- package/lib/module/web-core/AudioDecoder.web.js.map +1 -1
- package/lib/typescript/Audio/Audio.d.ts.map +1 -1
- package/lib/typescript/Audio/types.d.ts +5 -0
- package/lib/typescript/Audio/types.d.ts.map +1 -1
- package/lib/typescript/Audio/useAudioSourceLoader.d.ts +2 -1
- package/lib/typescript/Audio/useAudioSourceLoader.d.ts.map +1 -1
- package/lib/typescript/Audio/utils.d.ts.map +1 -1
- package/lib/typescript/api.d.ts +3 -2
- 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/AudioDecoder.d.ts +1 -2
- package/lib/typescript/core/AudioDecoder.d.ts.map +1 -1
- package/lib/typescript/core/AudioFileUtils.d.ts +3 -3
- package/lib/typescript/core/AudioFileUtils.d.ts.map +1 -1
- package/lib/typescript/jsi-interfaces.d.ts +1 -2
- package/lib/typescript/jsi-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/lib/typescript/specs/NativeAudioAPIModule.d.ts +1 -0
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.web.d.ts +1 -0
- package/lib/typescript/specs/NativeAudioAPIModule.web.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +2 -1
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/utils/flags.d.ts +9 -0
- package/lib/typescript/utils/flags.d.ts.map +1 -0
- package/lib/typescript/utils/index.d.ts +2 -0
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/lib/typescript/utils/paths.d.ts +1 -0
- package/lib/typescript/utils/paths.d.ts.map +1 -1
- package/lib/typescript/utils/remoteHttpSource.d.ts +7 -0
- package/lib/typescript/utils/remoteHttpSource.d.ts.map +1 -0
- package/lib/typescript/web-core/AudioDecoder.web.d.ts +1 -1
- package/lib/typescript/web-core/AudioDecoder.web.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Audio/Audio.tsx +2 -0
- package/src/Audio/types.ts +5 -0
- package/src/Audio/useAudioSourceLoader.ts +25 -15
- package/src/Audio/utils.ts +4 -0
- package/src/api.ts +3 -6
- package/src/api.web.ts +4 -0
- package/src/core/AudioDecoder.ts +3 -52
- package/src/core/AudioFileUtils.ts +66 -16
- package/src/jsi-interfaces.ts +3 -3
- package/src/mock/index.ts +5 -16
- package/src/specs/NativeAudioAPIModule.ts +1 -0
- package/src/specs/NativeAudioAPIModule.web.ts +2 -0
- package/src/types.ts +2 -1
- package/src/utils/flags.ts +12 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/paths.ts +11 -0
- package/src/utils/remoteHttpSource.ts +87 -0
- package/src/web-core/AudioDecoder.web.ts +10 -7
- package/lib/commonjs/utils/metadataPrefetching.js +0 -67
- package/lib/commonjs/utils/metadataPrefetching.js.map +0 -1
- package/lib/module/utils/metadataPrefetching.js +0 -61
- package/lib/module/utils/metadataPrefetching.js.map +0 -1
- package/lib/typescript/utils/metadataPrefetching.d.ts +0 -16
- package/lib/typescript/utils/metadataPrefetching.d.ts.map +0 -1
- package/src/utils/metadataPrefetching.ts +0 -96
package/android/build.gradle
CHANGED
|
@@ -63,8 +63,8 @@ void AndroidFileWriterBackend::cleanupPreallocatedInputPool() {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
void AndroidFileWriterBackend::writeAudioData(void *data, int numFrames) {
|
|
66
|
-
if (offloader_ == nullptr || freeSlots_ == nullptr ||
|
|
67
|
-
inputBuffers_.empty()) {
|
|
66
|
+
if (!isFileOpen() || offloader_ == nullptr || freeSlots_ == nullptr ||
|
|
67
|
+
inputBufferBytesPerSlot_ == 0 || inputBuffers_.empty()) {
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -108,7 +108,9 @@ CloseFileResult FFmpegAudioFileWriter::closeFile() {
|
|
|
108
108
|
return CloseFileResult::Err("File is not open");
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
isFileOpen_.store(false, std::memory_order_release);
|
|
112
|
+
|
|
113
|
+
// Drains the worker queue before flushing/finalizing the encoder below.
|
|
112
114
|
cleanupPreallocatedInputPool();
|
|
113
115
|
|
|
114
116
|
result = processFifo(true);
|
|
@@ -466,10 +468,12 @@ CloseFileResult FFmpegAudioFileWriter::finalizeOutput() {
|
|
|
466
468
|
avio_closep(&formatCtx_->pb);
|
|
467
469
|
}
|
|
468
470
|
|
|
469
|
-
double durationInSeconds =
|
|
471
|
+
double durationInSeconds = 0.0;
|
|
472
|
+
if (encoderCtx_ != nullptr && encoderCtx_->sample_rate > 0) {
|
|
473
|
+
durationInSeconds = static_cast<double>(nextPts_) / encoderCtx_->sample_rate;
|
|
474
|
+
}
|
|
470
475
|
|
|
471
476
|
filePath_ = "";
|
|
472
|
-
isFileOpen_.store(false, std::memory_order_release);
|
|
473
477
|
|
|
474
478
|
return CloseFileResult::Ok({fileSizeInMB, durationInSeconds});
|
|
475
479
|
}
|
package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.cpp
CHANGED
|
@@ -156,11 +156,11 @@ CloseFileResult MiniAudioFileWriter::closeFile() {
|
|
|
156
156
|
return CloseFileResult ::Err("File is not open");
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
// Joins the worker before the encoder teardown below.
|
|
160
|
-
cleanupPreallocatedInputPool();
|
|
161
|
-
|
|
162
159
|
isFileOpen_.store(false, std::memory_order_release);
|
|
163
160
|
|
|
161
|
+
// Drains the worker queue before the encoder teardown below.
|
|
162
|
+
cleanupPreallocatedInputPool();
|
|
163
|
+
|
|
164
164
|
if (encoder_ != nullptr) {
|
|
165
165
|
ma_encoder_uninit(encoder_.get());
|
|
166
166
|
encoder_.reset();
|
|
@@ -109,6 +109,8 @@ class AudioAPIModule(
|
|
|
109
109
|
|
|
110
110
|
override fun getDevicePreferredSampleRate(): Double = MediaSessionManager.getDevicePreferredSampleRate()
|
|
111
111
|
|
|
112
|
+
override fun isFfmpegEnabled(): Boolean = !BuildConfig.RN_AUDIO_API_FFMPEG_DISABLED
|
|
113
|
+
|
|
112
114
|
override fun setAudioSessionActivity(
|
|
113
115
|
enabled: Boolean,
|
|
114
116
|
promise: Promise?,
|
|
@@ -43,6 +43,10 @@ public abstract class NativeAudioAPIModuleSpec extends ReactContextBaseJavaModul
|
|
|
43
43
|
@DoNotStrip
|
|
44
44
|
public abstract double getDevicePreferredSampleRate();
|
|
45
45
|
|
|
46
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
47
|
+
@DoNotStrip
|
|
48
|
+
public abstract boolean isFfmpegEnabled();
|
|
49
|
+
|
|
46
50
|
@ReactMethod
|
|
47
51
|
@DoNotStrip
|
|
48
52
|
public abstract void setAudioSessionActivity(boolean enabled, Promise promise);
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
#include <audioapi/HostObjects/utils/JsEnumParser.h>
|
|
24
24
|
#include <audioapi/HostObjects/utils/NodeOptionsParser.h>
|
|
25
25
|
#include <audioapi/core/BaseAudioContext.h>
|
|
26
|
-
#include <audioapi/core/utils/AudioDecoding.
|
|
26
|
+
#include <audioapi/core/utils/AudioDecoding.h>
|
|
27
27
|
|
|
28
28
|
#include <memory>
|
|
29
29
|
#include <vector>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
|
|
2
2
|
#include <audioapi/HostObjects/utils/AudioDecoderHostObject.h>
|
|
3
|
-
#include <audioapi/core/utils/AudioDecoding.
|
|
3
|
+
#include <audioapi/core/utils/AudioDecoding.h>
|
|
4
4
|
#include <audioapi/jsi/JsiPromise.h>
|
|
5
5
|
|
|
6
6
|
#include <jsi/jsi.h>
|
|
@@ -16,7 +16,6 @@ AudioDecoderHostObject::AudioDecoderHostObject(
|
|
|
16
16
|
addFunctions(
|
|
17
17
|
JSI_EXPORT_FUNCTION(AudioDecoderHostObject, decodeWithPCMInBase64),
|
|
18
18
|
JSI_EXPORT_FUNCTION(AudioDecoderHostObject, decodeWithFilePath),
|
|
19
|
-
JSI_EXPORT_FUNCTION(AudioDecoderHostObject, getDurationWithFilePath),
|
|
20
19
|
JSI_EXPORT_FUNCTION(AudioDecoderHostObject, decodeWithMemoryBlock));
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -77,28 +76,6 @@ JSI_HOST_FUNCTION_IMPL(AudioDecoderHostObject, decodeWithFilePath) {
|
|
|
77
76
|
return promise;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
JSI_HOST_FUNCTION_IMPL(AudioDecoderHostObject, getDurationWithFilePath) {
|
|
81
|
-
auto sourcePath = args[0].getString(runtime).utf8(runtime);
|
|
82
|
-
|
|
83
|
-
auto promise = promiseVendor_->createAsyncPromise([sourcePath]() -> PromiseResolver {
|
|
84
|
-
auto result = audiodecoding::getDurationWithFilePath(sourcePath);
|
|
85
|
-
|
|
86
|
-
if (result.is_err()) {
|
|
87
|
-
return [result = std::move(result)](
|
|
88
|
-
jsi::Runtime &runtime) -> std::variant<jsi::Value, std::string> {
|
|
89
|
-
return result.unwrap_err();
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const auto duration = result.unwrap();
|
|
94
|
-
return [duration](jsi::Runtime &runtime) -> std::variant<jsi::Value, std::string> {
|
|
95
|
-
return jsi::Value(duration);
|
|
96
|
-
};
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return promise;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
79
|
JSI_HOST_FUNCTION_IMPL(AudioDecoderHostObject, decodeWithPCMInBase64) {
|
|
103
80
|
auto b64 = args[0].getString(runtime).utf8(runtime);
|
|
104
81
|
auto inputSampleRate = static_cast<float>(args[1].getNumber());
|
|
@@ -16,7 +16,6 @@ class AudioDecoderHostObject : public JsiHostObject {
|
|
|
16
16
|
const std::shared_ptr<react::CallInvoker> &callInvoker);
|
|
17
17
|
JSI_HOST_FUNCTION_DECL(decodeWithMemoryBlock);
|
|
18
18
|
JSI_HOST_FUNCTION_DECL(decodeWithFilePath);
|
|
19
|
-
JSI_HOST_FUNCTION_DECL(getDurationWithFilePath);
|
|
20
19
|
JSI_HOST_FUNCTION_DECL(decodeWithPCMInBase64);
|
|
21
20
|
|
|
22
21
|
private:
|
|
@@ -1,40 +1,19 @@
|
|
|
1
1
|
#include <audioapi/HostObjects/utils/AudioFileUtilsHostObject.h>
|
|
2
|
+
#include <audioapi/HostObjects/utils/NodeOptionsParser.h>
|
|
3
|
+
#include <audioapi/core/utils/AudioDecoding.h>
|
|
2
4
|
#include <audioapi/core/utils/AudioFileConcatenator.h>
|
|
3
|
-
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
4
|
-
#include <audioapi/libs/ffmpeg/FFmpegDecoding.h>
|
|
5
|
-
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
6
|
-
#include <audioapi/core/utils/AudioDecoding.hpp>
|
|
7
5
|
#include <audioapi/jsi/JsiPromise.h>
|
|
8
|
-
#include <audioapi/libs/miniaudio/MiniAudioDecoding.h>
|
|
9
6
|
|
|
10
7
|
#include <jsi/jsi.h>
|
|
11
8
|
#include <cstdint>
|
|
9
|
+
#include <map>
|
|
12
10
|
#include <memory>
|
|
13
|
-
#include <optional>
|
|
14
11
|
#include <string>
|
|
15
12
|
#include <utility>
|
|
16
13
|
#include <vector>
|
|
17
14
|
|
|
18
15
|
namespace audioapi {
|
|
19
16
|
|
|
20
|
-
namespace {
|
|
21
|
-
|
|
22
|
-
std::optional<double> probeDurationWithDecoder(const uint8_t *data, size_t size, int sampleRate) {
|
|
23
|
-
auto duration =
|
|
24
|
-
audiodecoding::probeDuration<miniaudio_decoder::MiniAudioDecoder>(data, size, sampleRate);
|
|
25
|
-
if (duration.has_value()) {
|
|
26
|
-
return duration;
|
|
27
|
-
}
|
|
28
|
-
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
29
|
-
duration = audiodecoding::probeDuration<ffmpeg_decoder::FFmpegDecoder>(data, size, sampleRate);
|
|
30
|
-
return duration;
|
|
31
|
-
#else
|
|
32
|
-
return std::nullopt;
|
|
33
|
-
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
} // namespace
|
|
37
|
-
|
|
38
17
|
AudioFileUtilsHostObject::AudioFileUtilsHostObject(
|
|
39
18
|
jsi::Runtime *runtime,
|
|
40
19
|
const std::shared_ptr<react::CallInvoker> &callInvoker) {
|
|
@@ -85,38 +64,67 @@ JSI_HOST_FUNCTION_IMPL(AudioFileUtilsHostObject, concatAudioFiles) {
|
|
|
85
64
|
}
|
|
86
65
|
|
|
87
66
|
JSI_HOST_FUNCTION_IMPL(AudioFileUtilsHostObject, probeDuration) {
|
|
88
|
-
if (count < 1
|
|
89
|
-
throw jsi::JSError(runtime, "probeDuration expects an ArrayBuffer.");
|
|
67
|
+
if (count < 1) {
|
|
68
|
+
throw jsi::JSError(runtime, "probeDuration expects an ArrayBuffer or URL/path string.");
|
|
90
69
|
}
|
|
91
70
|
|
|
92
|
-
auto
|
|
93
|
-
|
|
94
|
-
|
|
71
|
+
const auto sampleRate =
|
|
72
|
+
count > 1 && args[1].isNumber() ? static_cast<int>(args[1].getNumber()) : 0;
|
|
73
|
+
|
|
74
|
+
if (args[0].isObject() && args[0].asObject(runtime).isArrayBuffer(runtime)) {
|
|
75
|
+
auto arrayBuffer = args[0].asObject(runtime).getArrayBuffer(runtime);
|
|
76
|
+
const auto *data = arrayBuffer.data(runtime);
|
|
77
|
+
const auto size = arrayBuffer.size(runtime);
|
|
78
|
+
|
|
79
|
+
auto promise = promiseVendor_->createAsyncPromise(
|
|
80
|
+
[bytes = std::vector<uint8_t>(data, data + size), sampleRate]() -> PromiseResolver {
|
|
81
|
+
auto result =
|
|
82
|
+
audiodecoding::probeDurationWithMemory(bytes.data(), bytes.size(), sampleRate);
|
|
83
|
+
if (result.is_err()) {
|
|
84
|
+
return [](jsi::Runtime &runtime) -> std::variant<jsi::Value, std::string> {
|
|
85
|
+
return jsi::Value::null();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return [duration = static_cast<double>(result.unwrap())](
|
|
90
|
+
jsi::Runtime &runtime) -> std::variant<jsi::Value, std::string> {
|
|
91
|
+
return jsi::Value(duration);
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return promise;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const auto size = arrayBuffer.size(runtime);
|
|
98
|
+
if (args[0].isString()) {
|
|
99
|
+
const auto pathOrUrl = args[0].asString(runtime).utf8(runtime);
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
std::map<std::string, std::string> headers;
|
|
102
|
+
if (count > 2 && args[2].isObject()) {
|
|
103
|
+
headers = audioapi::option_parser::parseHttpHeaders(runtime, args[2].asObject(runtime));
|
|
104
|
+
}
|
|
103
105
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
auto promise = promiseVendor_->createAsyncPromise(
|
|
107
|
+
[pathOrUrl, sampleRate, headers = std::move(headers)]() -> PromiseResolver {
|
|
108
|
+
auto result = audiodecoding::isHttpUrl(pathOrUrl)
|
|
109
|
+
? audiodecoding::probeDurationWithUrl(pathOrUrl, sampleRate, headers)
|
|
110
|
+
: audiodecoding::probeDurationWithFilePath(pathOrUrl);
|
|
111
|
+
|
|
112
|
+
if (result.is_err()) {
|
|
113
|
+
return [](jsi::Runtime &runtime) -> std::variant<jsi::Value, std::string> {
|
|
114
|
+
return jsi::Value::null();
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return [duration = static_cast<double>(result.unwrap())](
|
|
119
|
+
jsi::Runtime &runtime) -> std::variant<jsi::Value, std::string> {
|
|
120
|
+
return jsi::Value(duration);
|
|
110
121
|
};
|
|
111
|
-
}
|
|
122
|
+
});
|
|
112
123
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return jsi::Value(duration);
|
|
116
|
-
};
|
|
117
|
-
});
|
|
124
|
+
return promise;
|
|
125
|
+
}
|
|
118
126
|
|
|
119
|
-
|
|
127
|
+
throw jsi::JSError(runtime, "probeDuration expects an ArrayBuffer or URL/path string.");
|
|
120
128
|
}
|
|
121
129
|
|
|
122
130
|
} // namespace audioapi
|
|
@@ -6,15 +6,46 @@
|
|
|
6
6
|
#include <cstddef>
|
|
7
7
|
#include <cstdio>
|
|
8
8
|
#include <limits>
|
|
9
|
+
#include <map>
|
|
9
10
|
#include <memory>
|
|
11
|
+
#include <string>
|
|
10
12
|
#include <vector>
|
|
11
13
|
|
|
12
14
|
#include <audioapi/HostObjects/effects/PeriodicWaveHostObject.h>
|
|
13
15
|
#include <audioapi/HostObjects/sources/AudioBufferHostObject.h>
|
|
14
|
-
#include <audioapi/core/utils/AudioDecoding.
|
|
16
|
+
#include <audioapi/core/utils/AudioDecoding.h>
|
|
15
17
|
#include <audioapi/types/NodeOptions.h>
|
|
16
18
|
|
|
17
19
|
namespace audioapi::option_parser {
|
|
20
|
+
|
|
21
|
+
inline std::map<std::string, std::string> parseHttpHeaders(
|
|
22
|
+
jsi::Runtime &runtime,
|
|
23
|
+
const jsi::Object &headersObject) {
|
|
24
|
+
std::map<std::string, std::string> headers;
|
|
25
|
+
const auto propertyNames = headersObject.getPropertyNames(runtime);
|
|
26
|
+
const auto count = propertyNames.size(runtime);
|
|
27
|
+
for (size_t i = 0; i < count; ++i) {
|
|
28
|
+
const auto keyValue = propertyNames.getValueAtIndex(runtime, i);
|
|
29
|
+
if (!keyValue.isString()) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const auto key = keyValue.asString(runtime).utf8(runtime);
|
|
33
|
+
const auto value = headersObject.getProperty(runtime, key.c_str());
|
|
34
|
+
if (value.isString()) {
|
|
35
|
+
headers.emplace(key, value.asString(runtime).utf8(runtime));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return headers;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
inline void mergeHttpHeaders(
|
|
42
|
+
std::map<std::string, std::string> &target,
|
|
43
|
+
const std::map<std::string, std::string> &incoming) {
|
|
44
|
+
for (const auto &[key, value] : incoming) {
|
|
45
|
+
target[key] = value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
18
49
|
inline AudioNodeOptions parseAudioNodeOptions(
|
|
19
50
|
jsi::Runtime &runtime,
|
|
20
51
|
const jsi::Object &optionsObject) {
|
|
@@ -320,12 +351,29 @@ inline AudioFileSourceOptions parseAudioFileSourceOptions(
|
|
|
320
351
|
|
|
321
352
|
auto sourceValue = optionsObject.getProperty(runtime, "source");
|
|
322
353
|
if (sourceValue.isString()) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
354
|
+
const auto path = sourceValue.asString(runtime).utf8(runtime);
|
|
355
|
+
if (audiodecoding::isHttpUrl(path)) {
|
|
356
|
+
options.sourceUrl = path;
|
|
357
|
+
options.requiresFFmpeg = true;
|
|
358
|
+
} else {
|
|
359
|
+
options.filePath = path;
|
|
360
|
+
options.requiresFFmpeg =
|
|
361
|
+
audiodecoding::pathHasExtension(path, {".mp4", ".m4a", ".aac", ".m3u8"});
|
|
362
|
+
}
|
|
326
363
|
} else if (sourceValue.isObject()) {
|
|
327
364
|
auto sourceObj = sourceValue.asObject(runtime);
|
|
328
|
-
if (sourceObj.
|
|
365
|
+
if (sourceObj.hasProperty(runtime, "uri")) {
|
|
366
|
+
const auto uriValue = sourceObj.getProperty(runtime, "uri");
|
|
367
|
+
if (uriValue.isString()) {
|
|
368
|
+
options.sourceUrl = uriValue.asString(runtime).utf8(runtime);
|
|
369
|
+
options.requiresFFmpeg = true;
|
|
370
|
+
}
|
|
371
|
+
const auto sourceHeadersValue = sourceObj.getProperty(runtime, "headers");
|
|
372
|
+
if (sourceHeadersValue.isObject()) {
|
|
373
|
+
mergeHttpHeaders(
|
|
374
|
+
options.httpHeaders, parseHttpHeaders(runtime, sourceHeadersValue.asObject(runtime)));
|
|
375
|
+
}
|
|
376
|
+
} else if (sourceObj.isArrayBuffer(runtime)) {
|
|
329
377
|
auto arrayBuffer = sourceObj.getArrayBuffer(runtime);
|
|
330
378
|
auto *data = arrayBuffer.data(runtime);
|
|
331
379
|
auto size = arrayBuffer.size(runtime);
|
|
@@ -336,11 +384,17 @@ inline AudioFileSourceOptions parseAudioFileSourceOptions(
|
|
|
336
384
|
}
|
|
337
385
|
}
|
|
338
386
|
|
|
387
|
+
const auto headersValue = optionsObject.getProperty(runtime, "headers");
|
|
388
|
+
if (headersValue.isObject()) {
|
|
389
|
+
mergeHttpHeaders(
|
|
390
|
+
options.httpHeaders, parseHttpHeaders(runtime, headersValue.asObject(runtime)));
|
|
391
|
+
}
|
|
392
|
+
|
|
339
393
|
#if RN_AUDIO_API_FFMPEG_DISABLED
|
|
340
394
|
if (options.requiresFFmpeg) {
|
|
341
395
|
throw jsi::JSError(
|
|
342
396
|
runtime,
|
|
343
|
-
"AudioFileSourceNode:
|
|
397
|
+
"AudioFileSourceNode: remote URLs and formats (.mp4, .m4a, .aac, .m3u8) require FFmpeg, "
|
|
344
398
|
"which is disabled in this build.");
|
|
345
399
|
}
|
|
346
400
|
#endif
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
#include <audioapi/core/sources/StreamerNode.h>
|
|
22
22
|
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
23
23
|
#include <audioapi/core/sources/WorkletSourceNode.h>
|
|
24
|
-
#include <audioapi/core/utils/AudioDecoding.
|
|
24
|
+
#include <audioapi/core/utils/AudioDecoding.h>
|
|
25
25
|
#include <audioapi/core/utils/AudioGraphManager.h>
|
|
26
26
|
#include <audioapi/core/utils/worklets/SafeIncludes.h>
|
|
27
27
|
#include <audioapi/events/AudioEventHandlerRegistry.h>
|
|
@@ -43,9 +43,12 @@ AudioFileSourceNode::AudioFileSourceNode(
|
|
|
43
43
|
|
|
44
44
|
const bool useFilePath = !options.filePath.empty();
|
|
45
45
|
const bool useData = !options.data.empty();
|
|
46
|
+
const bool useUrl = !options.sourceUrl.empty();
|
|
46
47
|
|
|
47
|
-
if (!useFilePath && !useData) {
|
|
48
|
-
assert(
|
|
48
|
+
if (!useFilePath && !useData && !useUrl) {
|
|
49
|
+
assert(
|
|
50
|
+
false &&
|
|
51
|
+
"AudioFileSourceNode requires a file path, remote URL, or memory data to initialize");
|
|
49
52
|
return;
|
|
50
53
|
}
|
|
51
54
|
|
|
@@ -103,6 +106,8 @@ bool AudioFileSourceNode::initDecoder(
|
|
|
103
106
|
SeekDecoderDaemonOptions daemonOptions{
|
|
104
107
|
.requiresFFmpeg = options.requiresFFmpeg,
|
|
105
108
|
.filePath = std::move(options.filePath),
|
|
109
|
+
.sourceUrl = std::move(options.sourceUrl),
|
|
110
|
+
.httpHeaders = std::move(options.httpHeaders),
|
|
106
111
|
.memoryData = std::move(options.data),
|
|
107
112
|
.contextSampleRate = context->getSampleRate(),
|
|
108
113
|
.loop = options.loop};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#include <audioapi/core/utils/AudioDecoding.
|
|
1
|
+
#include <audioapi/core/utils/AudioDecoding.h>
|
|
2
2
|
#include <audioapi/libs/base64/base64.h>
|
|
3
3
|
#include <audioapi/libs/decoding/IncrementalAudioDecoder.h>
|
|
4
4
|
#include <audioapi/libs/miniaudio/MiniAudioDecoding.h>
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include <cmath>
|
|
13
13
|
#include <cstdint>
|
|
14
14
|
#include <cstring>
|
|
15
|
+
#include <map>
|
|
15
16
|
#include <memory>
|
|
16
17
|
#include <string>
|
|
17
18
|
#include <utility>
|
|
@@ -109,71 +110,114 @@ bool pathHasExtension(const std::string &path, const std::vector<std::string> &e
|
|
|
109
110
|
extensions, [&pathLower](const std::string &ext) { return pathLower.ends_with(ext); });
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
bool
|
|
113
|
-
return
|
|
113
|
+
bool isHttpUrl(const std::string &path) {
|
|
114
|
+
return path.starts_with("http://") || path.starts_with("https://");
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (!isValidDuration(duration)) {
|
|
119
|
-
return Err("Audio duration metadata is unavailable");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return Ok(duration);
|
|
117
|
+
bool isValidDuration(float duration) {
|
|
118
|
+
return duration >= 0.0F && std::isfinite(duration);
|
|
123
119
|
}
|
|
124
120
|
|
|
125
|
-
|
|
126
|
-
const int sr = static_cast<int>(sampleRate);
|
|
127
|
-
|
|
121
|
+
AudioDurationResult probeDurationWithFilePath(const std::string &path) {
|
|
128
122
|
if (needsFFmpegByPath(path)) {
|
|
129
123
|
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
130
124
|
ffmpeg_decoder::FFmpegDecoder decoder;
|
|
131
|
-
const auto openResult = decoder.openFile(
|
|
125
|
+
const auto openResult = decoder.openFile(0, path);
|
|
132
126
|
if (openResult.is_err()) {
|
|
133
127
|
return Err("Failed to open file with FFmpeg decoder: " + openResult.unwrap_err());
|
|
134
128
|
}
|
|
135
|
-
auto result =
|
|
129
|
+
auto result = resolveDurationFromDecoder(decoder);
|
|
136
130
|
decoder.close();
|
|
137
131
|
return result;
|
|
138
132
|
#else
|
|
139
|
-
return Err("FFmpeg is disabled, cannot
|
|
133
|
+
return Err("FFmpeg is disabled, cannot inspect duration with file path");
|
|
140
134
|
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
141
135
|
}
|
|
142
136
|
|
|
143
137
|
miniaudio_decoder::MiniAudioDecoder decoder;
|
|
144
|
-
const auto openResult = decoder.openFile(
|
|
138
|
+
const auto openResult = decoder.openFile(0, path);
|
|
145
139
|
if (openResult.is_err()) {
|
|
146
140
|
return Err("Failed to open file with miniaudio decoder: " + openResult.unwrap_err());
|
|
147
141
|
}
|
|
148
|
-
auto result =
|
|
142
|
+
auto result = resolveDurationFromDecoder(decoder);
|
|
143
|
+
decoder.close();
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
AudioDurationResult probeDurationWithMemory(const void *data, size_t size, int sampleRate) {
|
|
148
|
+
const int sr = sampleRate != 0 ? sampleRate : 0;
|
|
149
|
+
|
|
150
|
+
{
|
|
151
|
+
miniaudio_decoder::MiniAudioDecoder decoder;
|
|
152
|
+
const auto openResult = decoder.openMemory(sr, data, size);
|
|
153
|
+
if (openResult.is_ok()) {
|
|
154
|
+
auto result = resolveDurationFromDecoder(decoder);
|
|
155
|
+
decoder.close();
|
|
156
|
+
if (result.is_ok()) {
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
163
|
+
ffmpeg_decoder::FFmpegDecoder decoder;
|
|
164
|
+
const auto openResult = decoder.openMemory(sr, data, size);
|
|
165
|
+
if (openResult.is_err()) {
|
|
166
|
+
return Err("Failed to probe duration from memory: " + openResult.unwrap_err());
|
|
167
|
+
}
|
|
168
|
+
auto result = resolveDurationFromDecoder(decoder);
|
|
169
|
+
decoder.close();
|
|
170
|
+
return result;
|
|
171
|
+
#else
|
|
172
|
+
return Err("Failed to probe duration from memory");
|
|
173
|
+
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
AudioDurationResult probeDurationWithUrl(
|
|
177
|
+
const std::string &url,
|
|
178
|
+
int sampleRate,
|
|
179
|
+
const std::map<std::string, std::string> &headers) {
|
|
180
|
+
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
181
|
+
ffmpeg_decoder::FFmpegDecoder decoder;
|
|
182
|
+
const auto openResult = decoder.openUrl(sampleRate, url, headers);
|
|
183
|
+
if (openResult.is_err()) {
|
|
184
|
+
return Err("Failed to open URL with FFmpeg decoder: " + openResult.unwrap_err());
|
|
185
|
+
}
|
|
186
|
+
auto result = resolveDurationFromDecoder(decoder);
|
|
149
187
|
decoder.close();
|
|
150
188
|
return result;
|
|
189
|
+
#else
|
|
190
|
+
(void)url;
|
|
191
|
+
(void)sampleRate;
|
|
192
|
+
(void)headers;
|
|
193
|
+
return Err("FFmpeg is disabled, cannot probe duration from URL");
|
|
194
|
+
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
151
195
|
}
|
|
152
196
|
|
|
153
|
-
|
|
154
|
-
|
|
197
|
+
AudioBufferResult decodeWithFilePath(const std::string &path, float sampleRate) {
|
|
198
|
+
const int sr = static_cast<int>(sampleRate);
|
|
155
199
|
|
|
156
200
|
if (needsFFmpegByPath(path)) {
|
|
157
201
|
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
158
202
|
ffmpeg_decoder::FFmpegDecoder decoder;
|
|
159
|
-
const auto openResult = decoder.openFile(
|
|
203
|
+
const auto openResult = decoder.openFile(sr, path);
|
|
160
204
|
if (openResult.is_err()) {
|
|
161
205
|
return Err("Failed to open file with FFmpeg decoder: " + openResult.unwrap_err());
|
|
162
206
|
}
|
|
163
|
-
auto result =
|
|
207
|
+
auto result = decodeAll(decoder);
|
|
164
208
|
decoder.close();
|
|
165
209
|
return result;
|
|
166
210
|
#else
|
|
167
|
-
return Err("FFmpeg is disabled, cannot
|
|
211
|
+
return Err("FFmpeg is disabled, cannot decode with file path");
|
|
168
212
|
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
169
213
|
}
|
|
170
214
|
|
|
171
215
|
miniaudio_decoder::MiniAudioDecoder decoder;
|
|
172
|
-
const auto openResult = decoder.openFile(
|
|
216
|
+
const auto openResult = decoder.openFile(sr, path);
|
|
173
217
|
if (openResult.is_err()) {
|
|
174
218
|
return Err("Failed to open file with miniaudio decoder: " + openResult.unwrap_err());
|
|
175
219
|
}
|
|
176
|
-
auto result =
|
|
220
|
+
auto result = decodeAll(decoder);
|
|
177
221
|
decoder.close();
|
|
178
222
|
return result;
|
|
179
223
|
}
|
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
#include <audioapi/utils/AudioBuffer.hpp>
|
|
6
6
|
#include <audioapi/utils/Result.hpp>
|
|
7
7
|
#include <cstddef>
|
|
8
|
+
#include <map>
|
|
8
9
|
#include <memory>
|
|
9
|
-
#include <optional>
|
|
10
10
|
#include <string>
|
|
11
|
-
#include <type_traits>
|
|
12
11
|
#include <vector>
|
|
13
12
|
|
|
14
13
|
namespace audioapi::audiodecoding {
|
|
@@ -17,7 +16,6 @@ using AudioBufferResult = Result<std::shared_ptr<AudioBuffer>, std::string>;
|
|
|
17
16
|
using AudioDurationResult = Result<float, std::string>;
|
|
18
17
|
|
|
19
18
|
[[nodiscard]] AudioBufferResult decodeWithFilePath(const std::string &path, float sampleRate);
|
|
20
|
-
[[nodiscard]] AudioDurationResult getDurationWithFilePath(const std::string &path);
|
|
21
19
|
[[nodiscard]] AudioBufferResult
|
|
22
20
|
decodeWithMemoryBlock(const void *data, size_t size, float sampleRate);
|
|
23
21
|
[[nodiscard]] AudioBufferResult decodeWithPCMInBase64(
|
|
@@ -32,8 +30,18 @@ decodeWithMemoryBlock(const void *data, size_t size, float sampleRate);
|
|
|
32
30
|
const std::string &path,
|
|
33
31
|
const std::vector<std::string> &extensions);
|
|
34
32
|
|
|
33
|
+
[[nodiscard]] bool isHttpUrl(const std::string &path);
|
|
35
34
|
[[nodiscard]] bool isValidDuration(float duration);
|
|
36
35
|
|
|
36
|
+
[[nodiscard]] inline AudioDurationResult resolveDurationFromDecoder(
|
|
37
|
+
decoding::IncrementalAudioDecoder &decoder) {
|
|
38
|
+
const float duration = decoder.getDurationInSeconds();
|
|
39
|
+
if (!isValidDuration(duration)) {
|
|
40
|
+
return Err("Audio duration metadata is unavailable");
|
|
41
|
+
}
|
|
42
|
+
return Ok(duration);
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
[[nodiscard]] inline bool needsFFmpeg(AudioFormat format) {
|
|
38
46
|
return format == AudioFormat::MP4 || format == AudioFormat::M4A || format == AudioFormat::AAC;
|
|
39
47
|
}
|
|
@@ -46,15 +54,12 @@ decodeWithMemoryBlock(const void *data, size_t size, float sampleRate);
|
|
|
46
54
|
return static_cast<float>(static_cast<int16_t>((byte2 << CHAR_BIT) | byte1)) / INT16_MAX;
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
return static_cast<double>(decoder.getDurationInSeconds());
|
|
58
|
-
}
|
|
57
|
+
[[nodiscard]] AudioDurationResult probeDurationWithFilePath(const std::string &path);
|
|
58
|
+
[[nodiscard]] AudioDurationResult
|
|
59
|
+
probeDurationWithMemory(const void *data, size_t size, int sampleRate = 0);
|
|
60
|
+
[[nodiscard]] AudioDurationResult probeDurationWithUrl(
|
|
61
|
+
const std::string &url,
|
|
62
|
+
int sampleRate = 0,
|
|
63
|
+
const std::map<std::string, std::string> &headers = {});
|
|
59
64
|
|
|
60
65
|
} // namespace audioapi::audiodecoding
|
|
@@ -18,7 +18,7 @@ SeekDecoderDaemon::SeekDecoderDaemon(
|
|
|
18
18
|
commandReceiver_(std::move(commandReceiver)),
|
|
19
19
|
frameSender_(std::move(frameSender)),
|
|
20
20
|
frameReceiverForDrain_(std::move(frameReceiver)) {
|
|
21
|
-
if (options.requiresFFmpeg) {
|
|
21
|
+
if (options.requiresFFmpeg || !options.sourceUrl.empty()) {
|
|
22
22
|
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
23
23
|
decoder_ = std::make_unique<ffmpeg_decoder::FFmpegDecoder>();
|
|
24
24
|
#endif
|
|
@@ -30,9 +30,9 @@ SeekDecoderDaemon::SeekDecoderDaemon(
|
|
|
30
30
|
|
|
31
31
|
int contextSampleRate = static_cast<int>(options.contextSampleRate);
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!options.filePath.empty()) {
|
|
33
|
+
if (!options.sourceUrl.empty()) {
|
|
34
|
+
openResult = decoder_->openUrl(contextSampleRate, options.sourceUrl, options.httpHeaders);
|
|
35
|
+
} else if (!options.filePath.empty()) {
|
|
36
36
|
openResult = decoder_->openFile(contextSampleRate, options.filePath);
|
|
37
37
|
} else {
|
|
38
38
|
openResult = decoder_->openMemory(
|