├── coverage ├── lcov.info ├── coverage-final.json ├── lcov-report │ ├── favicon.png │ ├── sort-arrow-sprite.png │ ├── prettify.css │ ├── block-navigation.js │ ├── index.html │ ├── base.css │ ├── sorter.js │ └── Component.tsx.html └── clover.xml ├── .husky ├── .npmignore ├── pre-commit └── commit-msg ├── example ├── src │ ├── components │ │ ├── Button │ │ │ ├── index.ts │ │ │ ├── Button.tsx │ │ │ └── Button.styles.tsx │ │ ├── LiveBadge │ │ │ ├── index.ts │ │ │ ├── LiveBadge.tsx │ │ │ └── LiveBadge.styles.tsx │ │ └── MicrophoneSelectModal │ │ │ ├── index.ts │ │ │ ├── MicrophoneSelectModal.styles.tsx │ │ │ └── MicrophoneSelectModal.tsx │ ├── App.styles.tsx │ ├── hooks │ │ └── usePermissions.ts │ └── App.tsx ├── app.json ├── ios │ ├── File.swift │ ├── assets │ │ └── app.json │ ├── RtmpExample │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── AppDelegate.mm │ ├── RtmpExample-Bridging-Header.h │ ├── RtmpExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── .xcode.env │ ├── Podfile │ └── RtmpExample.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ └── RtmpExample.xcscheme ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ └── drawable │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── jni │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── MainApplicationModuleProvider.h │ │ │ │ │ ├── OnLoad.cpp │ │ │ │ │ ├── MainComponentsRegistry.h │ │ │ │ │ ├── MainApplicationModuleProvider.cpp │ │ │ │ │ ├── MainApplicationTurboModuleManagerDelegate.h │ │ │ │ │ ├── MainApplicationTurboModuleManagerDelegate.cpp │ │ │ │ │ └── MainComponentsRegistry.cpp │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── reactnativertmp │ │ │ │ │ ├── newarchitecture │ │ │ │ │ ├── components │ │ │ │ │ │ └── MainComponentsRegistry.java │ │ │ │ │ ├── modules │ │ │ │ │ │ └── MainApplicationTurboModuleManagerDelegate.java │ │ │ │ │ └── MainApplicationReactNativeHost.java │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ └── debug │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── reactnativertmp │ │ │ │ └── ReactNativeFlipper.java │ │ ├── debug.keystore │ │ └── proguard-rules.pro │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── gradle.properties │ ├── build.gradle │ ├── gradlew.bat │ └── gradlew ├── index.tsx ├── babel.config.js ├── README.md ├── package.json └── metro.config.js ├── .gitattributes ├── tsconfig.build.json ├── babel.config.js ├── jest.config.js ├── .yarnrc ├── android ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── reactnativertmppublisher │ │ │ ├── enums │ │ │ ├── AudioInputType.java │ │ │ ├── StreamState.java │ │ │ └── BluetoothDeviceStatuses.java │ │ │ ├── interfaces │ │ │ └── ConnectionListener.java │ │ │ ├── utils │ │ │ └── ObjectCaster.java │ │ │ ├── RTMPPackage.java │ │ │ ├── modules │ │ │ ├── SurfaceHolderHelper.java │ │ │ ├── ConnectionChecker.java │ │ │ ├── BluetoothDeviceConnector.java │ │ │ └── Publisher.java │ │ │ ├── RTMPManager.java │ │ │ └── RTMPModule.java │ │ └── AndroidManifest.xml ├── build.gradle ├── gradlew.bat └── gradlew ├── 200197084-5e8ebcf2-320e-49cf-8504-0ad94773c218.png ├── ios ├── Podfile ├── RtmpPublish-Bridging-Header.h ├── RtmpPublish.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── RTMPManager │ ├── RTMPViewManager.swift │ ├── RTMPViewManager.m │ └── RTMPView.swift ├── RTMPModule │ ├── RTMPModule.swift │ └── RTMPModule.m └── RTMPCreator.swift ├── src ├── index.tsx ├── test │ ├── __snapshots__ │ │ └── RTMPPublisher.test.tsx.snap │ └── RTMPPublisher.test.tsx ├── Component.tsx ├── types.ts └── RTMPPublisher.tsx ├── .editorconfig ├── __mocks__ └── RTMPPublisher.js ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.yml └── workflows │ └── build.yml ├── react-native-rtmp-publisher.podspec ├── tsconfig.json ├── scripts └── bootstrap.js ├── .gitignore ├── LICENSE ├── package.json └── CODE_OF_CONDUCT.md /coverage/lcov.info: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.husky/.npmignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /coverage/coverage-final.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/src/components/Button/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Button'; 2 | -------------------------------------------------------------------------------- /example/src/components/LiveBadge/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './LiveBadge'; 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RtmpExample", 3 | "displayName": "Rtmp Example" 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "extends": "./tsconfig", 4 | "exclude": ["example"] 5 | } 6 | -------------------------------------------------------------------------------- /example/ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // RtmpExample 4 | // 5 | 6 | import Foundation 7 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint && yarn typescript 5 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/ios/assets/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RtmpExample", 3 | "displayName": "Rtmp Example" 4 | } 5 | -------------------------------------------------------------------------------- /example/src/components/MicrophoneSelectModal/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './MicrophoneSelectModal'; 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint -E HUSKY_GIT_PARAMS 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | setupFiles: ['./__mocks__/RTMPPublisher.js'], 4 | }; 5 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Rtmp Example 3 | 4 | -------------------------------------------------------------------------------- /coverage/lcov-report/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/coverage/lcov-report/favicon.png -------------------------------------------------------------------------------- /example/ios/RtmpExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/ios/RtmpExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /coverage/lcov-report/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/coverage/lcov-report/sort-arrow-sprite.png -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /200197084-5e8ebcf2-320e-49cf-8504-0ad94773c218.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/200197084-5e8ebcf2-320e-49cf-8504-0ad94773c218.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | use_frameworks! 3 | 4 | target 'RtmpPublish' do 5 | platform :ios, '10.0' 6 | 7 | # Pods for RtmpPublish 8 | 9 | end 10 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativertmppublisher/enums/AudioInputType.java: -------------------------------------------------------------------------------- 1 | package com.reactnativertmppublisher.enums; 2 | 3 | public enum AudioInputType { 4 | BLUETOOTH_HEADSET, 5 | SPEAKER 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/index.tsx: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './src/App'; 3 | import { name as appName } from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ezranbayantemur/react-native-rtmp-publisher/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativertmppublisher/enums/StreamState.java: -------------------------------------------------------------------------------- 1 | package com.reactnativertmppublisher.enums; 2 | 3 | public enum StreamState { 4 | CONNECTING, 5 | CONNECTED, 6 | DISCONNECTED, 7 | FAILED 8 | } 9 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativertmppublisher/enums/BluetoothDeviceStatuses.java: -------------------------------------------------------------------------------- 1 | package com.reactnativertmppublisher.enums; 2 | 3 | public enum BluetoothDeviceStatuses { 4 | CONNECTING, 5 | CONNECTED, 6 | DISCONNECTED 7 | } 8 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './RTMPPublisher'; 2 | export { 3 | RTMPPublisherProps, 4 | RTMPPublisherRefProps, 5 | StreamState, 6 | StreamStatus, 7 | AudioInputType, 8 | BluetoothDeviceStatuses, 9 | } from './types'; 10 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ios/RtmpPublish-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | #import "React/RCTViewManager.h" 7 | #import "React/RCTEventEmitter.h" 8 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativertmppublisher/interfaces/ConnectionListener.java: -------------------------------------------------------------------------------- 1 | package com.reactnativertmppublisher.interfaces; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | public interface ConnectionListener { 6 | void onChange(String type, @Nullable Object data); 7 | } 8 | -------------------------------------------------------------------------------- /ios/RtmpPublish.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/RtmpPublish.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/RtmpExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/RtmpExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/jni/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | # Define the library name here. 4 | project(reactnativertmp_appmodules) 5 | 6 | # This file includes all the necessary to let you build your application with the New Architecture. 7 | include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake) 8 | -------------------------------------------------------------------------------- /ios/RTMPManager/RTMPViewManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RTMPViewManager.swift 3 | // rtmpPackageExample 4 | // 5 | // Created by Ezran Bayantemur on 15.01.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | @objc(RTMPPublisherManager) 11 | class RTMPViewManager: RCTViewManager { 12 | override func view() -> UIView! { 13 | return RTMPView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/src/components/LiveBadge/LiveBadge.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text } from 'react-native'; 3 | 4 | import styles from './LiveBadge.styles'; 5 | 6 | const LiveBadge = () => { 7 | return ( 8 | 9 | 10 | LIVE 11 | 12 | ); 13 | }; 14 | 15 | export default LiveBadge; 16 | -------------------------------------------------------------------------------- /coverage/clover.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/jni/MainApplicationModuleProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace facebook { 9 | namespace react { 10 | 11 | std::shared_ptr MainApplicationModuleProvider( 12 | const std::string &moduleName, 13 | const JavaTurboModule::InitParams ¶ms); 14 | 15 | } // namespace react 16 | } // namespace facebook 17 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | presets: ['module:metro-react-native-babel-preset'], 6 | plugins: [ 7 | [ 8 | 'module-resolver', 9 | { 10 | extensions: ['.tsx', '.ts', '.js', '.json'], 11 | alias: { 12 | [pak.name]: path.join(__dirname, '..', pak.source), 13 | }, 14 | }, 15 | ], 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /example/android/app/src/main/jni/OnLoad.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MainApplicationTurboModuleManagerDelegate.h" 3 | #include "MainComponentsRegistry.h" 4 | 5 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { 6 | return facebook::jni::initialize(vm, [] { 7 | facebook::react::MainApplicationTurboModuleManagerDelegate:: 8 | registerNatives(); 9 | facebook::react::MainComponentsRegistry::registerNatives(); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /example/ios/RtmpExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /example/ios/RtmpExample/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /__mocks__/RTMPPublisher.js: -------------------------------------------------------------------------------- 1 | import { NativeModules } from 'react-native'; 2 | 3 | NativeModules.RTMPPublisher = { 4 | startStream: jest.fn(), 5 | stopStream: jest.fn(), 6 | isStreaming: jest.fn(), 7 | isCameraOnPreview: jest.fn(), 8 | getPublishURL: jest.fn(), 9 | hasCongestion: jest.fn(), 10 | isAudioPrepared: jest.fn(), 11 | isVideoPrepared: jest.fn(), 12 | isMuted: jest.fn(), 13 | mute: jest.fn(), 14 | unmute: jest.fn(), 15 | switchCamera: jest.fn(), 16 | toggleFlash: jest.fn(), 17 | }; 18 | -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | # NODE_BINARY variable contains the PATH to the node executable. 6 | # 7 | # Customize the NODE_BINARY variable here. 8 | # For example, to use nvm with brew, add the following line 9 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 10 | export NODE_BINARY=$(command -v node) -------------------------------------------------------------------------------- /src/test/__snapshots__/RTMPPublisher.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`RTMPPublisher tests should match with snapshot 1`] = ` 4 | 16 | `; 17 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/src/components/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TouchableOpacity, Text } from 'react-native'; 3 | 4 | import styles from './Button.styles'; 5 | 6 | type ButtonType = 'default' | 'circle'; 7 | 8 | export interface ButtonProps { 9 | title: string; 10 | type?: ButtonType; 11 | onPress: () => void; 12 | } 13 | 14 | const Button = ({ title, type = 'default', onPress }: ButtonProps) => { 15 | return ( 16 | 17 | {title} 18 | 19 | ); 20 | }; 21 | 22 | export default Button; 23 | -------------------------------------------------------------------------------- /example/src/components/LiveBadge/LiveBadge.styles.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export default StyleSheet.create({ 4 | container: { 5 | backgroundColor: '#eceff1', 6 | alignItems: 'center', 7 | justifyContent: 'center', 8 | flexDirection: 'row', 9 | padding: 5, 10 | borderRadius: 10, 11 | position: 'absolute', 12 | margin: 10, 13 | borderColor: '#bdbdbd', 14 | borderWidth: 1, 15 | }, 16 | title: { 17 | color: '#000', 18 | fontWeight: 'bold', 19 | }, 20 | dot: { 21 | backgroundColor: 'red', 22 | padding: 6, 23 | borderRadius: 20, 24 | marginRight: 5, 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /coverage/lcov-report/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /react-native-rtmp-publisher.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = "react-native-rtmp-publisher" 7 | s.version = package["version"] 8 | s.summary = package["description"] 9 | s.homepage = package["homepage"] 10 | s.license = package["license"] 11 | s.authors = package["author"] 12 | 13 | s.platforms = { :ios => "10.0" } 14 | s.source = { :git => "https://github.com/ezranbayantemur/react-native-rtmp-publisher.git", :tag => "#{s.version}" } 15 | 16 | s.source_files = "ios/**/*.{h,m,mm,swift}" 17 | 18 | s.dependency "React-Core" 19 | s.dependency "HaishinKit", "~> 1.2.7" 20 | 21 | end 22 | -------------------------------------------------------------------------------- /example/src/components/Button/Button.styles.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export default { 4 | default: StyleSheet.create({ 5 | container: { 6 | backgroundColor: '#039be5', 7 | alignItems: 'center', 8 | justifyContent: 'center', 9 | padding: 5, 10 | borderRadius: 5, 11 | }, 12 | title: { 13 | color: '#FFFFFF', 14 | }, 15 | }), 16 | 17 | circle: StyleSheet.create({ 18 | container: { 19 | backgroundColor: '#039be5', 20 | alignItems: 'center', 21 | justifyContent: 'center', 22 | padding: 8, 23 | borderRadius: 50, 24 | margin: 5, 25 | }, 26 | title: { 27 | color: '#FFFFFF', 28 | fontSize: 20, 29 | }, 30 | }), 31 | }; 32 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example Project 2 | 3 | Clone the repo and run 4 | 5 | ```sh 6 | yarn 7 | ``` 8 | and 9 | 10 | ```sh 11 | cd example && yarn ios (or yarn android) 12 | ``` 13 | You can specify your streaming URL's on the `App.tsx` file. 14 | ```tsx 15 | ... 16 | ... 17 | import MicrophoneSelectModal from './components/MicrophoneSelectModal'; 18 | 19 | const STREAM_URL = 'YOUR_STREAM_URL'; // ex: rtmp://a.rtmp.youtube.com/live2 20 | const STREAM_NAME = 'YOUR_STREAM_NAME'; // ex: abcd-1234-abcd-1234-abcd 21 | 22 | export default function App() { 23 | ... 24 | ... 25 | ``` 26 | ## Contributing 27 | 28 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. 29 | 30 | ## License 31 | 32 | MIT 33 | -------------------------------------------------------------------------------- /example/src/App.styles.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export default StyleSheet.create({ 4 | container: { 5 | flex: 1, 6 | }, 7 | publisher_camera: { 8 | flex: 1, 9 | width: '100%', 10 | height: '100%', 11 | }, 12 | footer_container: { 13 | position: 'absolute', 14 | bottom: 10, 15 | left: 0, 16 | right: 0, 17 | justifyContent: 'space-between', 18 | padding: 10, 19 | flexDirection: 'row', 20 | }, 21 | mute_container: { 22 | flex: 1, 23 | alignItems: 'flex-start', 24 | }, 25 | stream_container: { 26 | flex: 1, 27 | alignItems: 'center', 28 | }, 29 | controller_container: { 30 | flex: 1, 31 | flexDirection: 'row', 32 | justifyContent: 'flex-end', 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'RtmpExample' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../node_modules/react-native-gradle-plugin') 5 | if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") { 6 | include(":ReactAndroid") 7 | project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid') 8 | include(":ReactAndroid:hermes-engine") 9 | project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine') 10 | } 11 | include ':reactnativertmp' 12 | project(':reactnativertmp').projectDir = new File(rootProject.projectDir, '../../android') 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "react-native-rtmp-publisher": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "importsNotUsedAsValues": "error", 11 | "forceConsistentCasingInFileNames": true, 12 | "jsx": "react", 13 | "lib": ["esnext"], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": true, 18 | "noImplicitUseStrict": false, 19 | "noStrictGenericChecks": false, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "resolveJsonModule": true, 23 | "skipLibCheck": true, 24 | "strict": true, 25 | "target": "esnext" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-rtmp-publisher-example", 3 | "description": "Example app for react-native-rtmp-publisher", 4 | "version": "0.0.1", 5 | "private": true, 6 | "scripts": { 7 | "android": "react-native run-android", 8 | "ios": "react-native run-ios", 9 | "start": "react-native start" 10 | }, 11 | "dependencies": { 12 | "react": "18.1.0", 13 | "react-native": "0.70.6" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "^7.12.9", 17 | "@babel/runtime": "^7.12.5", 18 | "@react-native-community/eslint-config": "^2.0.0", 19 | "babel-jest": "^26.6.3", 20 | "babel-plugin-module-resolver": "^4.1.0", 21 | "eslint": "^7.32.0", 22 | "jest": "^26.6.3", 23 | "metro-react-native-babel-preset": "0.72.3", 24 | "react-test-renderer": "18.1.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/src/components/MicrophoneSelectModal/MicrophoneSelectModal.styles.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | const baseStyle = StyleSheet.create({ 4 | container: { 5 | flex: 1, 6 | justifyContent: 'flex-end', 7 | }, 8 | inner_container: { 9 | backgroundColor: 'white', 10 | padding: 10, 11 | }, 12 | footer_container: { 13 | margin: 10, 14 | }, 15 | }); 16 | 17 | const itemStyles = { 18 | not_selected: StyleSheet.create({ 19 | item_container: { 20 | padding: 5, 21 | }, 22 | item_title: {}, 23 | ...baseStyle, 24 | }), 25 | selected: StyleSheet.create({ 26 | item_container: { 27 | padding: 5, 28 | backgroundColor: '#eceff1', 29 | }, 30 | item_title: { 31 | fontWeight: 'bold', 32 | }, 33 | ...baseStyle, 34 | }), 35 | }; 36 | 37 | export default baseStyle; 38 | export { itemStyles }; 39 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const path = require('path'); 3 | const child_process = require('child_process'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const args = process.argv.slice(2); 7 | const options = { 8 | cwd: process.cwd(), 9 | env: process.env, 10 | stdio: 'inherit', 11 | encoding: 'utf-8', 12 | }; 13 | 14 | if (os.type() === 'Windows_NT') { 15 | options.shell = true; 16 | } 17 | 18 | let result; 19 | 20 | if (process.cwd() !== root || args.length) { 21 | // We're not in the root of the project, or additional arguments were passed 22 | // In this case, forward the command to `yarn` 23 | result = child_process.spawnSync('yarn', args, options); 24 | } else { 25 | // If `yarn` is run without arguments, perform bootstrap 26 | result = child_process.spawnSync('yarn', ['bootstrap'], options); 27 | } 28 | 29 | process.exitCode = result.status; 30 | -------------------------------------------------------------------------------- /ios/RTMPManager/RTMPViewManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // RTMPViewManager.m 3 | // rtmpPackageExample 4 | // 5 | // Created by Ezran Bayantemur on 15.01.2022. 6 | // 7 | 8 | #import 9 | 10 | @interface RCT_EXTERN_MODULE(RTMPPublisherManager, RCTViewManager) 11 | 12 | + (BOOL)requiresMainQueueSetup 13 | { 14 | return NO; 15 | } 16 | 17 | RCT_EXPORT_VIEW_PROPERTY(streamURL, NSString) 18 | 19 | RCT_EXPORT_VIEW_PROPERTY(streamName, NSString) 20 | 21 | RCT_EXPORT_VIEW_PROPERTY(onDisconnect, RCTDirectEventBlock) 22 | 23 | RCT_EXPORT_VIEW_PROPERTY(onConnectionFailed, RCTDirectEventBlock) 24 | 25 | RCT_EXPORT_VIEW_PROPERTY(onConnectionStarted, RCTDirectEventBlock) 26 | 27 | RCT_EXPORT_VIEW_PROPERTY(onConnectionSuccess, RCTDirectEventBlock) 28 | 29 | RCT_EXPORT_VIEW_PROPERTY(onNewBitrateReceived, RCTDirectEventBlock) 30 | 31 | RCT_EXPORT_VIEW_PROPERTY(onStreamStateChanged, RCTDirectEventBlock) 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativertmppublisher/utils/ObjectCaster.java: -------------------------------------------------------------------------------- 1 | package com.reactnativertmppublisher.utils; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.facebook.react.bridge.Arguments; 6 | import com.facebook.react.bridge.WritableMap; 7 | 8 | public class ObjectCaster { 9 | public static WritableMap caster(@Nullable Object data){ 10 | WritableMap event = Arguments.createMap(); 11 | 12 | if(data == null){ 13 | event.putNull("data"); 14 | } 15 | 16 | if(data instanceof String){ 17 | event.putString("data", (String) data); 18 | } 19 | 20 | if(data instanceof Integer){ 21 | event.putInt("data", (Integer) data); 22 | } 23 | 24 | if(data instanceof Boolean){ 25 | event.putBoolean("data", (Boolean) data); 26 | } 27 | 28 | if(data instanceof Long){ 29 | event.putDouble("data", (Long) data); 30 | } 31 | 32 | return event; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .classpath 35 | .cxx 36 | .gradle 37 | .idea 38 | .project 39 | .settings 40 | local.properties 41 | android.iml 42 | 43 | # Cocoapods 44 | # 45 | example/ios/Pods 46 | /ios/Pods 47 | 48 | # node.js 49 | # 50 | node_modules/ 51 | npm-debug.log 52 | yarn-debug.log 53 | yarn-error.log 54 | 55 | # BUCK 56 | buck-out/ 57 | \.buckd/ 58 | android/app/libs 59 | android/keystores/debug.keystore 60 | 61 | # Expo 62 | .expo/* 63 | 64 | # generated by bob 65 | lib/ 66 | example/.env 67 | .env 68 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativertmppublisher/RTMPPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactnativertmppublisher; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class RTMPPackage implements ReactPackage { 15 | @NonNull 16 | @Override 17 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 18 | List modules = new ArrayList<>(); 19 | modules.add(new RTMPModule(reactContext)); 20 | return modules; 21 | } 22 | 23 | @NonNull 24 | @Override 25 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 26 | return Collections.singletonList( 27 | new RTMPManager() 28 | ); 29 | } 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativertmppublisher/modules/SurfaceHolderHelper.java: -------------------------------------------------------------------------------- 1 | package com.reactnativertmppublisher.modules; 2 | 3 | 4 | import android.view.SurfaceHolder; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.facebook.react.uimanager.ThemedReactContext; 9 | import com.pedro.rtplibrary.rtmp.RtmpCamera1; 10 | 11 | public class SurfaceHolderHelper implements SurfaceHolder.Callback { 12 | private final RtmpCamera1 _rtmpCamera1; 13 | 14 | public SurfaceHolderHelper(ThemedReactContext reactContext, RtmpCamera1 rtmpCamera1, int surfaceId) { 15 | _rtmpCamera1 = rtmpCamera1; 16 | } 17 | 18 | @Override 19 | public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) { 20 | 21 | } 22 | 23 | @Override 24 | public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) { 25 | _rtmpCamera1.startPreview(1280 , 720); 26 | } 27 | 28 | @Override 29 | public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) { 30 | _rtmpCamera1.stopPreview(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/jni/MainComponentsRegistry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace facebook { 9 | namespace react { 10 | 11 | class MainComponentsRegistry 12 | : public facebook::jni::HybridClass { 13 | public: 14 | // Adapt it to the package you used for your Java class. 15 | constexpr static auto kJavaDescriptor = 16 | "Lcom/example/reactnativertmp/newarchitecture/components/MainComponentsRegistry;"; 17 | 18 | static void registerNatives(); 19 | 20 | MainComponentsRegistry(ComponentFactory *delegate); 21 | 22 | private: 23 | static std::shared_ptr 24 | sharedProviderRegistry(); 25 | 26 | static jni::local_ref initHybrid( 27 | jni::alias_ref, 28 | ComponentFactory *delegate); 29 | }; 30 | 31 | } // namespace react 32 | } // namespace facebook 33 | -------------------------------------------------------------------------------- /example/ios/RtmpExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "iphone", 5 | "scale": "2x", 6 | "size": "20x20" 7 | }, 8 | { 9 | "idiom": "iphone", 10 | "scale": "3x", 11 | "size": "20x20" 12 | }, 13 | { 14 | "idiom": "iphone", 15 | "scale": "2x", 16 | "size": "29x29" 17 | }, 18 | { 19 | "idiom": "iphone", 20 | "scale": "3x", 21 | "size": "29x29" 22 | }, 23 | { 24 | "idiom": "iphone", 25 | "scale": "2x", 26 | "size": "40x40" 27 | }, 28 | { 29 | "idiom": "iphone", 30 | "scale": "3x", 31 | "size": "40x40" 32 | }, 33 | { 34 | "idiom": "iphone", 35 | "scale": "2x", 36 | "size": "60x60" 37 | }, 38 | { 39 | "idiom": "iphone", 40 | "scale": "3x", 41 | "size": "60x60" 42 | }, 43 | { 44 | "idiom": "ios-marketing", 45 | "scale": "1x", 46 | "size": "1024x1024" 47 | } 48 | ], 49 | "info": { 50 | "author": "xcode", 51 | "version": 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ezran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/android/app/src/main/jni/MainApplicationModuleProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "MainApplicationModuleProvider.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace facebook { 7 | namespace react { 8 | 9 | std::shared_ptr MainApplicationModuleProvider( 10 | const std::string &moduleName, 11 | const JavaTurboModule::InitParams ¶ms) { 12 | // Here you can provide your own module provider for TurboModules coming from 13 | // either your application or from external libraries. The approach to follow 14 | // is similar to the following (for a library called `samplelibrary`: 15 | // 16 | // auto module = samplelibrary_ModuleProvider(moduleName, params); 17 | // if (module != nullptr) { 18 | // return module; 19 | // } 20 | // return rncore_ModuleProvider(moduleName, params); 21 | 22 | // Module providers autolinked by RN CLI 23 | auto rncli_module = rncli_ModuleProvider(moduleName, params); 24 | if (rncli_module != nullptr) { 25 | return rncli_module; 26 | } 27 | 28 | return rncore_ModuleProvider(moduleName, params); 29 | } 30 | 31 | } // namespace react 32 | } // namespace facebook 33 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const blacklist = require('metro-config/src/defaults/blacklist'); 3 | const escape = require('escape-string-regexp'); 4 | const pak = require('../package.json'); 5 | 6 | const root = path.resolve(__dirname, '..'); 7 | 8 | const modules = Object.keys({ 9 | ...pak.peerDependencies, 10 | }); 11 | 12 | module.exports = { 13 | projectRoot: __dirname, 14 | watchFolders: [root], 15 | 16 | // We need to make sure that only one version is loaded for peerDependencies 17 | // So we blacklist them at the root, and alias them to the versions in example's node_modules 18 | resolver: { 19 | blacklistRE: blacklist( 20 | modules.map( 21 | (m) => 22 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 23 | ) 24 | ), 25 | 26 | extraNodeModules: modules.reduce((acc, name) => { 27 | acc[name] = path.join(__dirname, 'node_modules', name); 28 | return acc; 29 | }, {}), 30 | }, 31 | 32 | transformer: { 33 | getTransformOptions: async () => ({ 34 | transform: { 35 | experimentalImportSupport: false, 36 | inlineRequires: true, 37 | }, 38 | }), 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace facebook { 8 | namespace react { 9 | 10 | class MainApplicationTurboModuleManagerDelegate 11 | : public jni::HybridClass< 12 | MainApplicationTurboModuleManagerDelegate, 13 | TurboModuleManagerDelegate> { 14 | public: 15 | // Adapt it to the package you used for your Java class. 16 | static constexpr auto kJavaDescriptor = 17 | "Lcom/example/reactnativertmp/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;"; 18 | 19 | static jni::local_ref initHybrid(jni::alias_ref); 20 | 21 | static void registerNatives(); 22 | 23 | std::shared_ptr getTurboModule( 24 | const std::string &name, 25 | const std::shared_ptr &jsInvoker) override; 26 | std::shared_ptr getTurboModule( 27 | const std::string &name, 28 | const JavaTurboModule::InitParams ¶ms) override; 29 | 30 | /** 31 | * Test-only method. Allows user to verify whether a TurboModule can be 32 | * created by instances of this class. 33 | */ 34 | bool canCreateTurboModule(const std::string &name); 35 | }; 36 | 37 | } // namespace react 38 | } // namespace facebook 39 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/reactnativertmp/newarchitecture/components/MainComponentsRegistry.java: -------------------------------------------------------------------------------- 1 | package com.example.reactnativertmp.newarchitecture.components; 2 | 3 | import com.facebook.jni.HybridData; 4 | import com.facebook.proguard.annotations.DoNotStrip; 5 | import com.facebook.react.fabric.ComponentFactory; 6 | import com.facebook.soloader.SoLoader; 7 | 8 | /** 9 | * Class responsible to load the custom Fabric Components. This class has native methods and needs a 10 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ 11 | * folder for you). 12 | * 13 | *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the 14 | * `newArchEnabled` property). Is ignored otherwise. 15 | */ 16 | @DoNotStrip 17 | public class MainComponentsRegistry { 18 | static { 19 | SoLoader.loadLibrary("fabricjni"); 20 | } 21 | 22 | @DoNotStrip private final HybridData mHybridData; 23 | 24 | @DoNotStrip 25 | private native HybridData initHybrid(ComponentFactory componentFactory); 26 | 27 | @DoNotStrip 28 | private MainComponentsRegistry(ComponentFactory componentFactory) { 29 | mHybridData = initHybrid(componentFactory); 30 | } 31 | 32 | @DoNotStrip 33 | public static MainComponentsRegistry register(ComponentFactory componentFactory) { 34 | return new MainComponentsRegistry(componentFactory); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/src/hooks/usePermissions.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { PermissionsAndroid, Platform } from 'react-native'; 3 | 4 | const CAMERA_PERMISSION = PermissionsAndroid.PERMISSIONS.CAMERA; 5 | const AUDIO_PERMISSION = PermissionsAndroid.PERMISSIONS.RECORD_AUDIO; 6 | const BLUETOOTH_PERMISSION = PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT; 7 | 8 | function usePermissions() { 9 | const [permissionGranted, setPermissionGranted] = useState(false); 10 | 11 | useEffect(() => { 12 | getPermissions(); 13 | }, []); 14 | 15 | async function getPermissions() { 16 | if (Platform.OS !== 'android') { 17 | setPermissionGranted(true); 18 | return; 19 | } 20 | 21 | const cameraPermission = await PermissionsAndroid.check(CAMERA_PERMISSION); 22 | const audioPermission = await PermissionsAndroid.check(AUDIO_PERMISSION); 23 | const bluetoothPermission = await PermissionsAndroid.check( 24 | BLUETOOTH_PERMISSION 25 | ); 26 | 27 | if (cameraPermission && audioPermission && bluetoothPermission) { 28 | return setPermissionGranted(true); 29 | } 30 | 31 | const hasPermissions = await PermissionsAndroid.requestMultiple([ 32 | CAMERA_PERMISSION, 33 | AUDIO_PERMISSION, 34 | BLUETOOTH_PERMISSION, 35 | ]); 36 | 37 | if (hasPermissions) { 38 | return setPermissionGranted(true); 39 | } 40 | } 41 | 42 | return { permissionGranted }; 43 | } 44 | 45 | export default usePermissions; 46 | -------------------------------------------------------------------------------- /src/Component.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | NativeSyntheticEvent, 3 | requireNativeComponent, 4 | ViewStyle, 5 | } from 'react-native'; 6 | import type { StreamState, BluetoothDeviceStatuses } from './types'; 7 | 8 | type RTMPData = { data: T }; 9 | 10 | export type ConnectionFailedType = NativeSyntheticEvent>; 11 | export type ConnectionStartedType = NativeSyntheticEvent>; 12 | export type ConnectionSuccessType = NativeSyntheticEvent>; 13 | export type DisconnectType = NativeSyntheticEvent>; 14 | export type NewBitrateReceivedType = NativeSyntheticEvent>; 15 | export type StreamStateChangedType = NativeSyntheticEvent< 16 | RTMPData 17 | >; 18 | export type BluetoothDeviceStatusChangedType = NativeSyntheticEvent< 19 | RTMPData 20 | >; 21 | export interface NativeRTMPPublisherProps { 22 | style?: ViewStyle; 23 | streamURL: string; 24 | streamName: string; 25 | onConnectionFailed?: (e: ConnectionFailedType) => void; 26 | onConnectionStarted?: (e: ConnectionStartedType) => void; 27 | onConnectionSuccess?: (e: ConnectionSuccessType) => void; 28 | onDisconnect?: (e: DisconnectType) => void; 29 | onNewBitrateReceived?: (e: NewBitrateReceivedType) => void; 30 | onStreamStateChanged?: (e: StreamStateChangedType) => void; 31 | onBluetoothDeviceStatusChanged?: ( 32 | e: BluetoothDeviceStatusChangedType 33 | ) => void; 34 | } 35 | export default requireNativeComponent( 36 | 'RTMPPublisher' 37 | ); 38 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | FLIPPER_VERSION=0.125.0 23 | # Use this property to specify which architecture you want to build. 24 | # You can also override it from the CLI using 25 | # ./gradlew -PreactNativeArchitectures=x86_64 26 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 27 | # Use this property to enable support to the new architecture. 28 | # This will allow you to use TurboModules and the Fabric render in 29 | # your application. You should enable this flag either if you want 30 | # to write custom TurboModules/Fabric components OR use libraries that 31 | # are providing them. 32 | newArchEnabled=false -------------------------------------------------------------------------------- /example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp: -------------------------------------------------------------------------------- 1 | #include "MainApplicationTurboModuleManagerDelegate.h" 2 | #include "MainApplicationModuleProvider.h" 3 | 4 | namespace facebook { 5 | namespace react { 6 | 7 | jni::local_ref 8 | MainApplicationTurboModuleManagerDelegate::initHybrid( 9 | jni::alias_ref) { 10 | return makeCxxInstance(); 11 | } 12 | 13 | void MainApplicationTurboModuleManagerDelegate::registerNatives() { 14 | registerHybrid({ 15 | makeNativeMethod( 16 | "initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid), 17 | makeNativeMethod( 18 | "canCreateTurboModule", 19 | MainApplicationTurboModuleManagerDelegate::canCreateTurboModule), 20 | }); 21 | } 22 | 23 | std::shared_ptr 24 | MainApplicationTurboModuleManagerDelegate::getTurboModule( 25 | const std::string &name, 26 | const std::shared_ptr &jsInvoker) { 27 | // Not implemented yet: provide pure-C++ NativeModules here. 28 | return nullptr; 29 | } 30 | 31 | std::shared_ptr 32 | MainApplicationTurboModuleManagerDelegate::getTurboModule( 33 | const std::string &name, 34 | const JavaTurboModule::InitParams ¶ms) { 35 | return MainApplicationModuleProvider(name, params); 36 | } 37 | 38 | bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule( 39 | const std::string &name) { 40 | return getTurboModule(name, nullptr) != nullptr || 41 | getTurboModule(name, {.moduleName = name}) != nullptr; 42 | } 43 | 44 | } // namespace react 45 | } // namespace facebook 46 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | if (project == rootProject) { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() 7 | } 8 | 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.5.3' 11 | } 12 | } 13 | } 14 | 15 | apply plugin: 'com.android.library' 16 | 17 | def safeExtGet(prop, fallback) { 18 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 19 | } 20 | 21 | android { 22 | compileSdkVersion safeExtGet('Rtmp_compileSdkVersion', 31) 23 | defaultConfig { 24 | minSdkVersion safeExtGet('Rtmp_minSdkVersion', 21) 25 | targetSdkVersion safeExtGet('Rtmp_targetSdkVersion', 31) 26 | versionCode 1 27 | versionName "1.0" 28 | 29 | } 30 | 31 | buildTypes { 32 | release { 33 | minifyEnabled false 34 | } 35 | } 36 | lintOptions { 37 | disable 'GradleCompatible' 38 | } 39 | compileOptions { 40 | sourceCompatibility JavaVersion.VERSION_1_8 41 | targetCompatibility JavaVersion.VERSION_1_8 42 | } 43 | } 44 | 45 | repositories { 46 | mavenLocal() 47 | maven { 48 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 49 | url("$rootDir/../node_modules/react-native/android") 50 | } 51 | google() 52 | mavenCentral() 53 | jcenter() 54 | maven { url 'https://www.jitpack.io' } 55 | } 56 | 57 | dependencies { 58 | //noinspection GradleDynamicVersion 59 | implementation fileTree(dir: 'libs', include: ['*.jar']) 60 | implementation 'com.github.pedroSG94.rtmp-rtsp-stream-client-java:rtplibrary:2.2.2' 61 | implementation "com.facebook.react:react-native:+" // From node_modules 62 | } 63 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/reactnativertmp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.reactnativertmp; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import com.facebook.react.ReactActivityDelegate; 5 | import com.facebook.react.ReactRootView; 6 | 7 | public class MainActivity extends ReactActivity { 8 | 9 | /** 10 | * Returns the name of the main component registered from JavaScript. This is used to schedule 11 | * rendering of the component. 12 | */ 13 | @Override 14 | protected String getMainComponentName() { 15 | return "RtmpExample"; 16 | } 17 | 18 | /** 19 | * Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and 20 | * you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer 21 | * (Paper). 22 | */ 23 | @Override 24 | protected ReactActivityDelegate createReactActivityDelegate() { 25 | return new MainActivityDelegate(this, getMainComponentName()); 26 | } 27 | public static class MainActivityDelegate extends ReactActivityDelegate { 28 | public MainActivityDelegate(ReactActivity activity, String mainComponentName) { 29 | super(activity, mainComponentName); 30 | } 31 | @Override 32 | protected ReactRootView createRootView() { 33 | ReactRootView reactRootView = new ReactRootView(getContext()); 34 | // If you opted-in for the New Architecture, we enable the Fabric Renderer. 35 | reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED); 36 | return reactRootView; 37 | } 38 | @Override 39 | protected boolean isConcurrentRootEnabled() { 40 | // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18). 41 | // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html 42 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | body: 4 | - type: textarea 5 | id: describe-the-bug 6 | attributes: 7 | label: Describe the bug 8 | description: A clear and concise description of what the bug is. 9 | validations: 10 | required: true 11 | - type: textarea 12 | id: to-reproduce 13 | attributes: 14 | label: To Reproduce 15 | description: Steps to reproduce the behavior 16 | placeholder: | 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: expected-behavior 25 | attributes: 26 | label: Expected behavior 27 | description: Steps to reproduce the behavior 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: version 32 | attributes: 33 | label: Version 34 | description: What version of our software are you running? 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: smartphone-info 39 | attributes: 40 | label: Smartphone info. 41 | description: please complete the following information 42 | placeholder: | 43 | - Device: [e.g. iPhone6] 44 | - OS: [e.g. iOS8.1] 45 | - type: textarea 46 | id: addditional-context 47 | attributes: 48 | label: Additional context 49 | description: Add any other context about the problem here. 50 | - type: textarea 51 | id: screenshot 52 | attributes: 53 | label: Screenshots 54 | description: If applicable, add screenshots to help explain your problem. 55 | - type: textarea 56 | id: logs 57 | attributes: 58 | label: Relevant log output 59 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 60 | render: shell -------------------------------------------------------------------------------- /example/ios/RtmpExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Rtmp Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | NSCameraUsageDescription 53 | Publishing the video 54 | NSMicrophoneUsageDescription 55 | Publishing the audio 56 | UIViewControllerBasedStatusBarAppearance 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "31.0.0" 6 | minSdkVersion = 21 7 | compileSdkVersion = 31 8 | targetSdkVersion = 31 9 | if (System.properties['os.arch'] == "aarch64") { 10 | // For M1 Users we need to use the NDK 24 which added support for aarch64 11 | ndkVersion = "24.0.8215888" 12 | } else { 13 | // Otherwise we default to the side-by-side NDK version from AGP. 14 | ndkVersion = "21.4.7075529" 15 | } 16 | } 17 | repositories { 18 | google() 19 | mavenCentral() 20 | // jcenter() 21 | } 22 | dependencies { 23 | classpath("com.android.tools.build:gradle:7.2.1") 24 | classpath("com.facebook.react:react-native-gradle-plugin") 25 | classpath("de.undercouch:gradle-download-task:5.0.1") 26 | // NOTE: Do not place your application dependencies here; they belong 27 | // in the individual module build.gradle files 28 | } 29 | } 30 | 31 | allprojects { 32 | repositories { 33 | // mavenLocal() 34 | maven { 35 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 36 | url("$rootDir/../node_modules/react-native/android") 37 | } 38 | maven { 39 | // Android JSC is installed from npm 40 | url("$rootDir/../node_modules/jsc-android/dist") 41 | } 42 | mavenCentral { 43 | // We don't want to fetch react-native from Maven Central as there are 44 | // older versions over there. 45 | content { 46 | excludeGroup "com.facebook.react" 47 | } 48 | } 49 | google() 50 | mavenCentral() 51 | // jcenter() 52 | maven { url 'https://www.jitpack.io' } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '12.4' 5 | install! 'cocoapods', :deterministic_uuids => false 6 | 7 | target 'RtmpExample' do 8 | config = use_native_modules! 9 | 10 | # Flags change depending on the env values. 11 | flags = get_default_flags() 12 | use_react_native!( 13 | :path => config[:reactNativePath], 14 | # Hermes is now enabled by default. Disable by setting this flag to false. 15 | # Upcoming versions of React Native may rely on get_default_flags(), but 16 | # we make it explicit here to aid in the React Native upgrade process. 17 | :hermes_enabled => true, 18 | :fabric_enabled => flags[:fabric_enabled], 19 | # Enables Flipper. 20 | # 21 | # Note that if you have use_frameworks! enabled, Flipper will not work and 22 | # you should disable the next line. 23 | :flipper_configuration => FlipperConfiguration.enabled, 24 | # An absolute path to your application root. 25 | :app_path => "#{Pod::Config.instance.installation_root}/.." 26 | ) 27 | 28 | pod 'react-native-rtmp-publisher', :path => '../..' 29 | 30 | # Enables Flipper. 31 | # 32 | # Note that if you have use_frameworks! enabled, Flipper will not work and 33 | # you should disable these next few lines. 34 | # use_flipper!({ 'Flipper' => '0.80.0' }) 35 | # post_install do |installer| 36 | # flipper_post_install(installer) 37 | # end 38 | 39 | post_install do |installer| 40 | react_native_post_install(installer) 41 | installer.pods_project.build_configurations.each do |config| config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" 42 | end 43 | 44 | react_native_post_install( 45 | installer, 46 | # Set `mac_catalyst_enabled` to `true` in order to apply patches 47 | # necessary for Mac Catalyst builds 48 | :mac_catalyst_enabled => false 49 | ) 50 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativertmppublisher/modules/ConnectionChecker.java: -------------------------------------------------------------------------------- 1 | package com.reactnativertmppublisher.modules; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.pedro.rtmp.utils.ConnectCheckerRtmp; 6 | import com.reactnativertmppublisher.interfaces.ConnectionListener; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class ConnectionChecker implements ConnectCheckerRtmp { 12 | private final List listeners = new ArrayList<>(); 13 | 14 | public void addListener(ConnectionListener listener) { 15 | listeners.add(listener); 16 | } 17 | 18 | @Override 19 | public void onAuthErrorRtmp() { 20 | for (ConnectionListener l : listeners) { 21 | l.onChange("onAuthError", null); 22 | } 23 | } 24 | 25 | @Override 26 | public void onAuthSuccessRtmp() { 27 | for (ConnectionListener l : listeners) { 28 | l.onChange("onAuthSuccess", null); 29 | } 30 | } 31 | 32 | // TODO: Parameters will be send after onChange method updated 33 | @Override 34 | public void onConnectionFailedRtmp(@NonNull String s) { 35 | for (ConnectionListener l : listeners) { 36 | l.onChange("onConnectionFailed", s); 37 | } 38 | } 39 | 40 | // TODO: Parameters will be send after onChange method updated 41 | @Override 42 | public void onConnectionStartedRtmp(@NonNull String s) { 43 | for (ConnectionListener l : listeners) { 44 | l.onChange("onConnectionStarted", s); 45 | } 46 | } 47 | 48 | @Override 49 | public void onConnectionSuccessRtmp() { 50 | for (ConnectionListener l : listeners) { 51 | l.onChange("onConnectionSuccess", null); 52 | } 53 | } 54 | 55 | @Override 56 | public void onDisconnectRtmp() { 57 | for (ConnectionListener l : listeners) { 58 | l.onChange("onDisconnect", null); 59 | } 60 | } 61 | 62 | // TODO: Parameters will be send after onChange method updated 63 | @Override 64 | public void onNewBitrateRtmp(long b) { 65 | for (ConnectionListener l : listeners) { 66 | l.onChange("onNewBitrateReceived", b); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/reactnativertmp/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java: -------------------------------------------------------------------------------- 1 | package com.example.reactnativertmp.newarchitecture.modules; 2 | 3 | import com.facebook.jni.HybridData; 4 | import com.facebook.react.ReactPackage; 5 | import com.facebook.react.ReactPackageTurboModuleManagerDelegate; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.soloader.SoLoader; 8 | import java.util.List; 9 | 10 | /** 11 | * Class responsible to load the TurboModules. This class has native methods and needs a 12 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ 13 | * folder for you). 14 | * 15 | *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the 16 | * `newArchEnabled` property). Is ignored otherwise. 17 | */ 18 | public class MainApplicationTurboModuleManagerDelegate 19 | extends ReactPackageTurboModuleManagerDelegate { 20 | 21 | private static volatile boolean sIsSoLibraryLoaded; 22 | 23 | protected MainApplicationTurboModuleManagerDelegate( 24 | ReactApplicationContext reactApplicationContext, List packages) { 25 | super(reactApplicationContext, packages); 26 | } 27 | 28 | protected native HybridData initHybrid(); 29 | 30 | native boolean canCreateTurboModule(String moduleName); 31 | 32 | public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder { 33 | protected MainApplicationTurboModuleManagerDelegate build( 34 | ReactApplicationContext context, List packages) { 35 | return new MainApplicationTurboModuleManagerDelegate(context, packages); 36 | } 37 | } 38 | 39 | @Override 40 | protected synchronized void maybeLoadOtherSoLibraries() { 41 | if (!sIsSoLibraryLoaded) { 42 | // If you change the name of your application .so file in the Android.mk file, 43 | // make sure you update the name here as well. 44 | SoLoader.loadLibrary("reactnativertmp_appmodules"); 45 | sIsSoLibraryLoaded = true; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { ViewStyle } from 'react-native'; 2 | 3 | export interface RTMPPublisherRefProps { 4 | /** 5 | * Starts stream operation 6 | */ 7 | startStream: () => Promise; 8 | /** 9 | * Stops stream operation 10 | */ 11 | stopStream: () => Promise; 12 | /** 13 | * Checks stream status 14 | */ 15 | isStreaming: () => Promise; 16 | /** 17 | * Checks if camera on mount 18 | */ 19 | isCameraOnPreview: () => Promise; 20 | /** 21 | * Gets settled publish url 22 | */ 23 | getPublishURL: () => Promise; 24 | /** 25 | * Checks congestion status 26 | */ 27 | hasCongestion: () => Promise; 28 | /** 29 | * Checks audio status 30 | */ 31 | isAudioPrepared: () => Promise; 32 | /** 33 | * Checks video status 34 | */ 35 | isVideoPrepared: () => Promise; 36 | /** 37 | * Checks if mic closed 38 | */ 39 | isMuted: () => Promise; 40 | /** 41 | * Mutes the mic 42 | */ 43 | mute: () => Promise; 44 | /** 45 | * Unmutes the mic 46 | */ 47 | unmute: () => Promise; 48 | /** 49 | * Switches the camera 50 | */ 51 | switchCamera: () => Promise; 52 | /** 53 | * Toggles the flash 54 | */ 55 | toggleFlash: () => Promise; 56 | /** 57 | * Sets the audio input (microphone type) 58 | */ 59 | setAudioInput: (audioInput: AudioInputType) => Promise; 60 | } 61 | 62 | export interface RTMPPublisherProps { 63 | style?: ViewStyle; 64 | streamURL: string; 65 | streamName: string; 66 | onConnectionFailed?: (e: null) => void; 67 | onConnectionStarted?: (e: null) => void; 68 | onConnectionSuccess?: (e: null) => void; 69 | onDisconnect?: (e: null) => void; 70 | onNewBitrateReceived?: (e: number) => void; 71 | onStreamStateChanged?: (e: StreamState) => void; 72 | } 73 | export type StreamStatus = 74 | | 'CONNECTING' 75 | | 'CONNECTED' 76 | | 'DISCONNECTED' 77 | | 'FAILED'; 78 | 79 | export enum StreamState { 80 | CONNECTING = 'CONNECTING', 81 | CONNECTED = 'CONNECTED', 82 | DISCONNECTED = 'DISCONNECTED', 83 | FAILED = 'FAILED', 84 | } 85 | export enum BluetoothDeviceStatuses { 86 | CONNECTING = 'CONNECTING', 87 | CONNECTED = 'CONNECTED', 88 | DISCONNECTED = 'DISCONNECTED', 89 | } 90 | 91 | export enum AudioInputType { 92 | BLUETOOTH_HEADSET = 0, 93 | SPEAKER = 1, 94 | WIRED_HEADSET = 2, 95 | } 96 | -------------------------------------------------------------------------------- /example/src/components/MicrophoneSelectModal/MicrophoneSelectModal.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, Text, Modal, TouchableOpacity, Platform } from 'react-native'; 3 | import { AudioInputType } from 'react-native-rtmp-publisher'; 4 | import Button from '../Button'; 5 | 6 | import styles, { itemStyles } from './MicrophoneSelectModal.styles'; 7 | 8 | export interface ButtonProps { 9 | visible: boolean; 10 | onClose: () => void; 11 | onSelect: (a: AudioInputType) => void; 12 | } 13 | 14 | const initialMicrophoneValues = [ 15 | { 16 | key: AudioInputType.SPEAKER, 17 | title: 'Speaker', 18 | }, 19 | { 20 | key: AudioInputType.BLUETOOTH_HEADSET, 21 | title: 'Bluetooth Headset', 22 | }, 23 | { 24 | key: AudioInputType.WIRED_HEADSET, 25 | title: 'Wired Headset', 26 | }, 27 | ]; 28 | 29 | const MicrophoneSelectModal = ({ visible, onSelect, onClose }: ButtonProps) => { 30 | const [selectedMicrophoneKey, setSelectedMicrophoneKey] = 31 | useState(); 32 | 33 | const handleSelect = (value: AudioInputType) => { 34 | onSelect(value); 35 | 36 | setSelectedMicrophoneKey(value); 37 | }; 38 | 39 | return ( 40 | 46 | 47 | 48 | {initialMicrophoneValues.map((value) => { 49 | if ( 50 | Platform.OS === 'android' && 51 | value.key === AudioInputType.WIRED_HEADSET 52 | ) { 53 | return; 54 | } 55 | 56 | const status = 57 | selectedMicrophoneKey === value.key ? 'selected' : 'not_selected'; 58 | 59 | return ( 60 | handleSelect(value.key)} 64 | > 65 | {value.title} 66 | 67 | ); 68 | })} 69 | 70 | 71 |