Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3216,7 +3216,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: c5c4f5280e4ae0f9f4a739c64c4260fe0b3edaf1
ReactCodegen: 096bbbb2498ca55f385e2fbd465bfa0211ee8295
ReactCommon: 25c7f94aee74ddd93a8287756a8ac0830a309544
RNAudioAPI: d8a5bc076cb04272fbb9eee48d6cce69394b62c0
RNAudioAPI: c763dbacdb8d89b7ce829484306df54322a7d951
RNGestureHandler: f1dd7f92a0faa2868a919ab53bb9d66eb4ebfcf5
RNReanimated: e4993dd98196c698cbacc1441a4ac5b855ae56dc
RNScreens: 833237c48c756d40764540246a501b47dadb2cac
Expand Down
8 changes: 5 additions & 3 deletions packages/audiodocs/docs/core/base-audio-context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,16 @@ Creates [`BiquadFilterNode`](/docs/effects/biquad-filter-node).

:::caution
Supported file formats:
- aac
- flac
- m4a
- mp3
- mp4
- ogg
- opus
- wav
- aac
- m4a
- mp4

Last three formats are decoded with ffmpeg, [see for more info](/docs/other/ffmpeg-info).
:::

### `decodeAudioData`
Expand Down
39 changes: 39 additions & 0 deletions packages/audiodocs/docs/other/ffmpeg-info.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
sidebar_position: 5
---

# FFmpeg additional information

We use [`ffmpeg`](https://github.com/FFmpeg/FFmpeg) for few components:
- [`StreamerNode`](/docs/sources/streamer-node)
- decoding `aac`, `mp4`, `m4a` files

## Disabling FFmpeg

The ffmpeg usage is enabled by default, however if you would like not to use it, f.e. there are some name clashes with other ffmpeg
binaries in your project, you can easily disable it. Just add one flag in corresponding file.

## Expo projects

Add entry in [expo plugin](/docs/fundamentals/getting-started#step-2-add-audio-api-expo-plugin-optional). If not provided, default
value is `false`.

```
"disableFFmpeg": true
```

## Non expo projects

### Android

gradle.properties
```
disableAudioapiFFmpeg=true
```

### iOS

Podfile
```
ENV['DISABLE_AUDIOAPI_FFMPEG'] = '1'
```
12 changes: 7 additions & 5 deletions packages/react-native-audio-api/RNAudioAPI.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ require_relative './scripts/rnaa_utils'
package_json = JSON.parse(File.read(File.join(__dir__, "package.json")))

$new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
$RN_AUDIO_API_FFMPEG_DISABLED = ENV['DISABLE_AUDIOAPI_FFMPEG'].nil? ? false : ENV['DISABLE_AUDIOAPI_FFMPEG'] == '1' # false by default

folly_flags = "-DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32"
fabric_flags = $new_arch_enabled ? '-DRCT_NEW_ARCH_ENABLED' : ''
version_flag = "-DAUDIOAPI_VERSION=#{package_json['version']}"

worklets_preprocessor_flag = check_if_worklets_enabled() ? '-DRN_AUDIO_API_ENABLE_WORKLETS=1' : ''
ffmpeg_flag = $RN_AUDIO_API_FFMPEG_DISABLED ? '-DRN_AUDIO_API_FFMPEG_DISABLED=1' : ''

Pod::Spec.new do |s|
s.name = "RNAudioAPI"
Expand All @@ -24,6 +26,7 @@ Pod::Spec.new do |s|

s.subspec "audioapi" do |ss|
ss.source_files = "common/cpp/audioapi/**/*.{cpp,c,h,hpp}"
ss.exclude_files = $RN_AUDIO_API_FFMPEG_DISABLED ? ["common/cpp/audioapi/libs/ffmpeg/**"] : []
ss.header_dir = "audioapi"
ss.header_mappings_dir = "common/cpp/audioapi"

Expand Down Expand Up @@ -57,7 +60,7 @@ Pod::Spec.new do |s|
external_dir_relative = "common/cpp/audioapi/external"
lib_dir = "$(PROJECT_DIR)/#{rn_audio_dir_relative}/#{external_dir_relative}/$(PLATFORM_NAME)"

s.ios.vendored_frameworks = [
s.ios.vendored_frameworks = $RN_AUDIO_API_FFMPEG_DISABLED ? [] : [
'common/cpp/audioapi/external/ffmpeg_ios/libavcodec.xcframework',
'common/cpp/audioapi/external/ffmpeg_ios/libavformat.xcframework',
'common/cpp/audioapi/external/ffmpeg_ios/libavutil.xcframework',
Expand All @@ -75,10 +78,9 @@ s.pod_target_xcconfig = {
$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include
$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include/opus
$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/include/vorbis
$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/ffmpeg_include
].join(" "),
'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag}",
'OTHER_CPLUSPLUSFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag}",
].concat($RN_AUDIO_API_FFMPEG_DISABLED ? [] : ["$(PODS_TARGET_SRCROOT)/#{external_dir_relative}/ffmpeg_include"]).join(" "),
'OTHER_CFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag} #{ffmpeg_flag}",
'OTHER_CPLUSPLUSFLAGS' => "$(inherited) #{folly_flags} #{fabric_flags} #{version_flag} #{worklets_preprocessor_flag} #{ffmpeg_flag}",
}

s.user_target_xcconfig = {
Expand Down
10 changes: 8 additions & 2 deletions packages/react-native-audio-api/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def isNewArchitectureEnabled() {
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
}

def isFFmpegDisabled() {
return rootProject.hasProperty("disableAudioapiFFmpeg") && rootProject.getProperty("disableAudioapiFFmpeg") == "true"
}

apply plugin: "com.android.library"
apply plugin: 'org.jetbrains.kotlin.android'

Expand Down Expand Up @@ -124,6 +128,7 @@ file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { rea
def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME")
def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled()
def IS_RN_AUDIO_API_FFMPEG_DISABLED = isFFmpegDisabled()

android {
sourceSets {
Expand Down Expand Up @@ -163,8 +168,9 @@ android {
"-DANDROID_TOOLCHAIN=clang",
"-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}",
"-DRN_AUDIO_API_WORKLETS_ENABLED=${isWorkletsAvailable}",
"-DIS_NEW_ARCHITECTURE_ENABLED=${IS_NEW_ARCHITECTURE_ENABLED}",
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
"-DIS_NEW_ARCHITECTURE_ENABLED=${IS_NEW_ARCHITECTURE_ENABLED}",
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
"-DRN_AUDIO_API_FFMPEG_DISABLED=${IS_RN_AUDIO_API_FFMPEG_DISABLED}"
]

if (isWorkletsAvailable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

#ifndef AUDIO_API_TEST_SUITE
#include <android/log.h>
#endif // AUDIO_API_TEST_SUITE
#if !RN_AUDIO_API_FFMPEG_DISABLED
#include <audioapi/libs/ffmpeg/FFmpegDecoding.h>
#endif
#endif // RN_AUDIO_API_FFMPEG_DISABLED

#include <memory>
#include <string>
Expand Down Expand Up @@ -73,13 +75,22 @@ std::shared_ptr<AudioBuffer> AudioDecoder::decodeWithFilePath(
float sampleRate) {
#ifndef AUDIO_API_TEST_SUITE
if (AudioDecoder::pathHasExtension(path, {".mp4", ".m4a", ".aac"})) {
#if !RN_AUDIO_API_FFMPEG_DISABLED
auto buffer = ffmpegdecoder::decodeWithFilePath(path, static_cast<int>(sampleRate));
if (buffer == nullptr) {
__android_log_print(
ANDROID_LOG_ERROR, "AudioDecoder", "Failed to decode with FFmpeg: %s", path.c_str());
return nullptr;
}
return buffer;
#else
__android_log_print(
ANDROID_LOG_ERROR,
"AudioDecoder",
"FFmpeg is disabled, cannot decode file: %s",
path.c_str());
return nullptr;
#endif // RN_AUDIO_API_FFMPEG_DISABLED
}
ma_decoder decoder;
ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, static_cast<int>(sampleRate));
Expand Down Expand Up @@ -115,12 +126,18 @@ AudioDecoder::decodeWithMemoryBlock(const void *data, size_t size, float sampleR
#ifndef AUDIO_API_TEST_SUITE
const AudioFormat format = AudioDecoder::detectAudioFormat(data, size);
if (format == AudioFormat::MP4 || format == AudioFormat::M4A || format == AudioFormat::AAC) {
#if !RN_AUDIO_API_FFMPEG_DISABLED
auto buffer = ffmpegdecoder::decodeWithMemoryBlock(data, size, static_cast<int>(sampleRate));
if (buffer == nullptr) {
__android_log_print(ANDROID_LOG_ERROR, "AudioDecoder", "Failed to decode with FFmpeg");
return nullptr;
}
return buffer;
#else
__android_log_print(
ANDROID_LOG_ERROR, "AudioDecoder", "FFmpeg is disabled, cannot decode memory block");
return nullptr;
#endif // RN_AUDIO_API_FFMPEG_DISABLED
}
ma_decoder decoder;
ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, static_cast<int>(sampleRate));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,15 @@ JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createOscillator) {
}

JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createStreamer) {
#if !RN_AUDIO_API_FFMPEG_DISABLED
auto streamer = context_->createStreamer();
auto streamerHostObject = std::make_shared<StreamerNodeHostObject>(streamer);
auto object = jsi::Object::createFromHostObject(runtime, streamerHostObject);
object.setExternalMemoryPressure(runtime, StreamerNodeHostObject::getSizeInBytes());
return object;
#else
return jsi::Value::undefined();
#endif // RN_AUDIO_API_FFMPEG_DISABLED
}

JSI_HOST_FUNCTION_IMPL(BaseAudioContextHostObject, createConstantSource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ StreamerNodeHostObject::StreamerNodeHostObject(const std::shared_ptr<StreamerNod
}

JSI_HOST_FUNCTION_IMPL(StreamerNodeHostObject, initialize) {
#if !RN_AUDIO_API_FFMPEG_DISABLED
auto streamerNode = std::static_pointer_cast<StreamerNode>(node_);
auto path = args[0].getString(runtime).utf8(runtime);
auto result = streamerNode->initialize(path);
return {result};
#else
return false;
#endif
}

} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
#include <audioapi/core/sources/ConstantSourceNode.h>
#include <audioapi/core/sources/OscillatorNode.h>
#include <audioapi/core/sources/RecorderAdapterNode.h>
#if !RN_AUDIO_API_FFMPEG_DISABLED
#include <audioapi/core/sources/StreamerNode.h>
#endif // RN_AUDIO_API_FFMPEG_DISABLED
#include <audioapi/core/sources/WorkletSourceNode.h>
#include <audioapi/core/utils/AudioDecoder.h>
#include <audioapi/core/utils/AudioNodeManager.h>
Expand Down Expand Up @@ -121,13 +123,15 @@ std::shared_ptr<ConstantSourceNode> BaseAudioContext::createConstantSource() {
return constantSource;
}

#ifndef AUDIO_API_TEST_SUITE
std::shared_ptr<StreamerNode> BaseAudioContext::createStreamer() {
#if !RN_AUDIO_API_FFMPEG_DISABLED
auto streamer = std::make_shared<StreamerNode>(this);
nodeManager_->addSourceNode(streamer);
return streamer;
#else
return nullptr;
#endif // RN_AUDIO_API_FFMPEG_DISABLED
}
#endif

std::shared_ptr<GainNode> BaseAudioContext::createGain() {
auto gain = std::make_shared<GainNode>(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <audioapi/core/sources/AudioScheduledSourceNode.h>
#include <audioapi/utils/AudioBus.h>

#ifndef AUDIO_API_TEST_SUITE
#if !RN_AUDIO_API_FFMPEG_DISABLED
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
Expand All @@ -22,7 +22,7 @@ extern "C" {
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
}
#endif
#endif // RN_AUDIO_API_FFMPEG_DISABLED

#include <atomic>
#include <cmath>
Expand All @@ -36,7 +36,7 @@ static constexpr audioapi::channels::spsc::OverflowStrategy STREAMER_NODE_SPSC_O
audioapi::channels::spsc::OverflowStrategy::WAIT_ON_FULL;
static constexpr audioapi::channels::spsc::WaitStrategy STREAMER_NODE_SPSC_WAIT_STRATEGY =
audioapi::channels::spsc::WaitStrategy::ATOMIC_WAIT;
#endif
#endif // AUDIO_API_TEST_SUITE

static constexpr bool VERBOSE = false;
static constexpr int CHANNEL_CAPACITY = 32;
Expand Down Expand Up @@ -78,7 +78,7 @@ class StreamerNode : public AudioScheduledSourceNode {
int framesToProcess) override;

private:
#ifndef AUDIO_API_TEST_SUITE
#if !RN_AUDIO_API_FFMPEG_DISABLED
AVFormatContext *fmtCtx_;
AVCodecContext *codecCtx_;
const AVCodec *decoder_;
Expand Down Expand Up @@ -149,6 +149,6 @@ class StreamerNode : public AudioScheduledSourceNode {
* @return true if successful, false otherwise
*/
bool setupDecoder();
#endif // AUDIO_API_TEST_SUITE
#endif // RN_AUDIO_API_FFMPEG_DISABLED
};
} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
*/

#include <audioapi/core/sources/AudioBuffer.h>
#if !RN_AUDIO_API_FFMPEG_DISABLED
#include <audioapi/libs/ffmpeg/FFmpegDecoding.h>
#endif // RN_AUDIO_API_FFMPEG_DISABLED
#include <audioapi/utils/AudioArray.h>
#include <audioapi/utils/AudioBus.h>
#include <functional>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ add_executable(
add_compile_definitions(AUDIO_API_TEST_SUITE)
add_compile_definitions(RN_AUDIO_API_ENABLE_WORKLETS=0)
add_compile_definitions(RN_AUDIO_API_TEST=1)
add_compile_definitions(RN_AUDIO_API_FFMPEG_DISABLED=1)

target_link_libraries(tests
rnaudioapi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include <audioapi/dsp/VectorMath.h>
#include <audioapi/libs/audio-stretch/stretch.h>
#include <audioapi/libs/base64/base64.h>
#if !RN_AUDIO_API_FFMPEG_DISABLED
#include <audioapi/libs/ffmpeg/FFmpegDecoding.h>
#endif // RN_AUDIO_API_FFMPEG_DISABLED
#include <audioapi/utils/AudioArray.h>
#include <audioapi/utils/AudioBus.h>

Expand Down Expand Up @@ -67,12 +69,17 @@
float sampleRate)
{
if (AudioDecoder::pathHasExtension(path, {".mp4", ".m4a", ".aac"})) {
#if !RN_AUDIO_API_FFMPEG_DISABLED
auto buffer = ffmpegdecoder::decodeWithFilePath(path, static_cast<int>(sampleRate));
if (buffer == nullptr) {
NSLog(@"Failed to decode with FFmpeg: %s", path.c_str());
return nullptr;
}
return buffer;
#else
NSLog(@"FFmpeg is disabled, cannot decode file: %s", path.c_str());
return nullptr;
#endif // RN_AUDIO_API_FFMPEG_DISABLED
}
ma_decoder decoder;
ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, static_cast<int>(sampleRate));
Expand Down Expand Up @@ -101,12 +108,17 @@
{
const AudioFormat format = AudioDecoder::detectAudioFormat(data, size);
if (format == AudioFormat::MP4 || format == AudioFormat::M4A || format == AudioFormat::AAC) {
#if !RN_AUDIO_API_FFMPEG_DISABLED
auto buffer = ffmpegdecoder::decodeWithMemoryBlock(data, size, static_cast<int>(sampleRate));
if (buffer == nullptr) {
NSLog(@"Failed to decode with FFmpeg");
return nullptr;
}
return buffer;
#else
NSLog(@"FFmpeg is disabled, cannot decode memory block");
return nullptr;
#endif // RN_AUDIO_API_FFMPEG_DISABLED
}
ma_decoder decoder;
ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, static_cast<int>(sampleRate));
Expand Down
6 changes: 5 additions & 1 deletion packages/react-native-audio-api/src/core/BaseAudioContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,11 @@ export default class BaseAudioContext {
}

createStreamer(): StreamerNode {
return new StreamerNode(this, this.context.createStreamer());
const streamer = this.context.createStreamer();
if (!streamer) {
throw new NotSupportedError('StreamerNode requires FFmpeg build');
}
return new StreamerNode(this, streamer);
}

createConstantSource(): ConstantSourceNode {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-audio-api/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export interface IBaseAudioContext {
buffer: IAudioBuffer | undefined,
disableNormalization: boolean
) => IConvolverNode;
createStreamer: () => IStreamerNode;
createStreamer: () => IStreamerNode | null; // null when FFmpeg is not enabled
}

export interface IAudioContext extends IBaseAudioContext {
Expand Down
Loading