react-native-audio-api 0.13.0-nightly-79cb732-20260630 → 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 -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 +87 -1
- package/common/cpp/audioapi/core/utils/{AudioDecoding.hpp → AudioDecoding.h} +21 -12
- 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 +15 -0
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/api.web.js +13 -0
- package/lib/commonjs/api.web.js.map +1 -1
- package/lib/commonjs/core/AudioDecoder.js +1 -11
- 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 +22 -1
- 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 +57 -0
- 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 +2 -1
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +4 -1
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/core/AudioDecoder.js +2 -12
- 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 +20 -1
- 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 +56 -0
- 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 +2 -1
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/api.web.d.ts +2 -1
- package/lib/typescript/api.web.d.ts.map +1 -1
- 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 -1
- package/lib/typescript/jsi-interfaces.d.ts.map +1 -1
- package/lib/typescript/mock/index.d.ts +7 -2
- 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 -0
- 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 +4 -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 +2 -1
- package/src/api.web.ts +5 -0
- package/src/core/AudioDecoder.ts +2 -14
- package/src/core/AudioFileUtils.ts +66 -16
- package/src/jsi-interfaces.ts +3 -2
- package/src/mock/index.ts +43 -0
- package/src/specs/NativeAudioAPIModule.ts +1 -0
- package/src/specs/NativeAudioAPIModule.web.ts +2 -0
- package/src/types.ts +2 -0
- 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 +86 -1
- 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>
|
|
@@ -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>
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
#include <algorithm>
|
|
11
11
|
#include <cctype>
|
|
12
|
+
#include <cmath>
|
|
12
13
|
#include <cstdint>
|
|
13
14
|
#include <cstring>
|
|
15
|
+
#include <map>
|
|
14
16
|
#include <memory>
|
|
15
17
|
#include <string>
|
|
16
18
|
#include <utility>
|
|
@@ -108,6 +110,90 @@ bool pathHasExtension(const std::string &path, const std::vector<std::string> &e
|
|
|
108
110
|
extensions, [&pathLower](const std::string &ext) { return pathLower.ends_with(ext); });
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
bool isHttpUrl(const std::string &path) {
|
|
114
|
+
return path.starts_with("http://") || path.starts_with("https://");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
bool isValidDuration(float duration) {
|
|
118
|
+
return duration >= 0.0F && std::isfinite(duration);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
AudioDurationResult probeDurationWithFilePath(const std::string &path) {
|
|
122
|
+
if (needsFFmpegByPath(path)) {
|
|
123
|
+
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
124
|
+
ffmpeg_decoder::FFmpegDecoder decoder;
|
|
125
|
+
const auto openResult = decoder.openFile(0, path);
|
|
126
|
+
if (openResult.is_err()) {
|
|
127
|
+
return Err("Failed to open file with FFmpeg decoder: " + openResult.unwrap_err());
|
|
128
|
+
}
|
|
129
|
+
auto result = resolveDurationFromDecoder(decoder);
|
|
130
|
+
decoder.close();
|
|
131
|
+
return result;
|
|
132
|
+
#else
|
|
133
|
+
return Err("FFmpeg is disabled, cannot inspect duration with file path");
|
|
134
|
+
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
miniaudio_decoder::MiniAudioDecoder decoder;
|
|
138
|
+
const auto openResult = decoder.openFile(0, path);
|
|
139
|
+
if (openResult.is_err()) {
|
|
140
|
+
return Err("Failed to open file with miniaudio decoder: " + openResult.unwrap_err());
|
|
141
|
+
}
|
|
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);
|
|
187
|
+
decoder.close();
|
|
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
|
|
195
|
+
}
|
|
196
|
+
|
|
111
197
|
AudioBufferResult decodeWithFilePath(const std::string &path, float sampleRate) {
|
|
112
198
|
const int sr = static_cast<int>(sampleRate);
|
|
113
199
|
|
|
@@ -5,15 +5,15 @@
|
|
|
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 {
|
|
15
14
|
|
|
16
15
|
using AudioBufferResult = Result<std::shared_ptr<AudioBuffer>, std::string>;
|
|
16
|
+
using AudioDurationResult = Result<float, std::string>;
|
|
17
17
|
|
|
18
18
|
[[nodiscard]] AudioBufferResult decodeWithFilePath(const std::string &path, float sampleRate);
|
|
19
19
|
[[nodiscard]] AudioBufferResult
|
|
@@ -30,6 +30,18 @@ decodeWithMemoryBlock(const void *data, size_t size, float sampleRate);
|
|
|
30
30
|
const std::string &path,
|
|
31
31
|
const std::vector<std::string> &extensions);
|
|
32
32
|
|
|
33
|
+
[[nodiscard]] bool isHttpUrl(const std::string &path);
|
|
34
|
+
[[nodiscard]] bool isValidDuration(float duration);
|
|
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
|
+
|
|
33
45
|
[[nodiscard]] inline bool needsFFmpeg(AudioFormat format) {
|
|
34
46
|
return format == AudioFormat::MP4 || format == AudioFormat::M4A || format == AudioFormat::AAC;
|
|
35
47
|
}
|
|
@@ -42,15 +54,12 @@ decodeWithMemoryBlock(const void *data, size_t size, float sampleRate);
|
|
|
42
54
|
return static_cast<float>(static_cast<int16_t>((byte2 << CHAR_BIT) | byte1)) / INT16_MAX;
|
|
43
55
|
}
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
return static_cast<double>(decoder.getDurationInSeconds());
|
|
54
|
-
}
|
|
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 = {});
|
|
55
64
|
|
|
56
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(
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
#include <audioapi/utils/SpscChannel.hpp>
|
|
11
11
|
#include <array>
|
|
12
12
|
#include <atomic>
|
|
13
|
+
#include <map>
|
|
13
14
|
#include <memory>
|
|
14
15
|
#include <optional>
|
|
15
16
|
#include <string>
|
|
@@ -21,6 +22,8 @@ struct SeekDecoderDaemonOptions {
|
|
|
21
22
|
bool requiresFFmpeg;
|
|
22
23
|
// source
|
|
23
24
|
std::string filePath;
|
|
25
|
+
std::string sourceUrl;
|
|
26
|
+
std::map<std::string, std::string> httpHeaders;
|
|
24
27
|
std::vector<uint8_t> memoryData;
|
|
25
28
|
// playback
|
|
26
29
|
float contextSampleRate;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <audioapi/utils/Macros.h>
|
|
4
4
|
#include <audioapi/utils/Result.hpp>
|
|
5
5
|
#include <cstddef>
|
|
6
|
+
#include <map>
|
|
6
7
|
#include <string>
|
|
7
8
|
|
|
8
9
|
namespace audioapi::decoding {
|
|
@@ -24,6 +25,16 @@ class IncrementalAudioDecoder {
|
|
|
24
25
|
/// @return Ok(None) on success or Err(message) on failure.
|
|
25
26
|
[[nodiscard]] virtual DecoderResult openFile(int outputSampleRate, const std::string &path) = 0;
|
|
26
27
|
|
|
28
|
+
/// @brief Opens a remote HTTP(S) URL for incremental decoding via FFmpeg.
|
|
29
|
+
/// @param outputSampleRate The output sample rate.
|
|
30
|
+
/// @param url The remote URL (http:// or https://).
|
|
31
|
+
/// @param headers Optional HTTP request headers (e.g. Authorization).
|
|
32
|
+
/// @return Ok(None) on success or Err(message) on failure.
|
|
33
|
+
[[nodiscard]] virtual DecoderResult openUrl(
|
|
34
|
+
int outputSampleRate,
|
|
35
|
+
const std::string &url,
|
|
36
|
+
const std::map<std::string, std::string> &headers = {}) = 0;
|
|
37
|
+
|
|
27
38
|
/// @brief Opens a memory block for decoding.
|
|
28
39
|
/// @param outputSampleRate The output sample rate.
|
|
29
40
|
/// @param data The data to decode.
|