Merge pull request #166 from tanersener/development-react-native

implement react-native plugin
This commit is contained in:
Taner Şener 2021-10-02 00:11:38 +01:00 committed by GitHub
commit 2ee5414365
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 6775 additions and 56 deletions

View File

@ -1,4 +1,4 @@
# FFmpegKit ![GitHub release](https://img.shields.io/badge/release-v4.5-blue.svg) ![Maven Central](https://img.shields.io/maven-central/v/com.arthenica/ffmpeg-kit-min) ![CocoaPods](https://img.shields.io/cocoapods/v/ffmpeg-kit-ios-min)
# FFmpegKit ![GitHub release](https://img.shields.io/badge/release-v4.5-blue.svg) ![Maven Central](https://img.shields.io/maven-central/v/com.arthenica/ffmpeg-kit-min) ![CocoaPods](https://img.shields.io/cocoapods/v/ffmpeg-kit-ios-min) [![npm](https://img.shields.io/npm/v/ffmpeg-kit-react-native.svg)](ffmpeg-kit-react-native)
FFmpeg Kit for applications.
@ -7,42 +7,48 @@ FFmpeg Kit for applications.
### 1. Features
- Scripts to build FFmpeg libraries
- `FFmpegKit` wrapper library to run `FFmpeg`/`FFprobe` commands in applications
- Supports Android, iOS, macOS and tvOS
- Supports native platforms: Android, iOS, macOS and tvOS
- Supports hybrid platforms: React Native
- Based on FFmpeg `v4.5-dev` with optional system and external libraries
- 8 prebuilt binary packages available at [Github](https://github.com/tanersener/ffmpeg-kit/releases), [Maven Central](https://search.maven.org) and [CocoaPods](https://cocoapods.org).
- 8 prebuilt binary packages available at [Github](https://github.com/tanersener/ffmpeg-kit/releases), [Maven Central](https://search.maven.org), [CocoaPods](https://cocoapods.org) and [npm](https://www.npmjs.com)
- Licensed under `LGPL 3.0`, or `GPL v3.0` if GPL licensed libraries are enabled
### 2. Android
See [Android](https://github.com/tanersener/ffmpeg-kit/tree/development/android) to learn more about `FFmpegKit` for
See [Android](https://github.com/tanersener/ffmpeg-kit/tree/main/android) to learn more about `FFmpegKit` for
`Android`.
### 3. iOS, macOS, tvOS
See [Apple](https://github.com/tanersener/ffmpeg-kit/tree/development/apple) to use `FFmpegKit` on `Apple` platforms
See [Apple](https://github.com/tanersener/ffmpeg-kit/tree/main/apple) to use `FFmpegKit` on `Apple` platforms
(`iOS`, `macOS`, `tvOS`).
### 4. Build Scripts
### 4. React Native
Use `android.sh`, `ios.sh`, `macos.sh` and `tvos.sh` to build `FFmpegKit` for each platform.
See [React Native](https://github.com/tanersener/ffmpeg-kit/tree/main/react-native) to learn more about `FFmpegKit` for
`React Native`.
### 5. Build Scripts
Use `android.sh`, `ios.sh`, `macos.sh` and `tvos.sh` to build `FFmpegKit` for each native platform.
All scripts support additional options to enable optional libraries and disable platform architectures.
### 5. FFmpegKit Library
### 6. FFmpegKit Library
`FFmpegKit` is a wrapper library that allows you to easily run `FFmpeg`/`FFprobe` commands in applications. It
provides additional features on top of `FFmpeg` to enable platform specific resources, control how commands are
executed and how the results are handled.
`Android` library has a `Java` API and `Apple` libraries (`iOS`, `macOS`, `tvOS`) have an `Objective-C` API,
which are identical in terms of features and capabilities.
`Android` library of `FFmpegKit` has a `Java` API, `Apple` libraries (`iOS`, `macOS`, `tvOS`) have an `Objective-C`
API and `React Native` library provides a `JavaScript` API, which are identical in terms of features and capabilities.
### 6. Binary Packages
### 7. Packages
There are eight different `ffmpeg-kit` packages distributed on
[Github](https://github.com/tanersener/ffmpeg-kit/releases),
[Maven Central](https://search.maven.org) and [CocoaPods](https://cocoapods.org).
Below you can see which system libraries and external libraries are enabled in each one of them.
[Maven Central](https://search.maven.org), [CocoaPods](https://cocoapods.org) and [npm](https://www.npmjs.com).
Below you can see which system libraries and external libraries are enabled in each one of them.
Please remember that some parts of `FFmpeg` are licensed under the `GPL` and only `GPL` licensed `ffmpeg-kit` packages
include them.
@ -96,21 +102,22 @@ include them.
- `AVFoundation` is not available on `tvOS`
- `VideoToolbox` is not available on LTS releases of `iOS` and `tvOS`
### 7. Versions
### 8. Versions
`FFmpegKit` binaries generated use the same major and minor version numbers as the upstream `FFmpeg` project.
The exact version number of `FFmpeg` is obtained using `git describe --tags`. `dev` part in the version string
indicates that `FFmpeg` source code is cloned from the `FFmpeg` `master` branch.
| FFmpegKit Version | FFmpeg Version | Release Date |
| :----: | :----: |:----: |
| [4.5](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.5) | 4.5-dev-2008 | Sep 18, 2021 |
| [4.5.LTS](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.5.LTS) | 4.5-dev-2008 | Sep 18, 2021 |
| [4.4](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.4) | 4.4-dev-3015 | Mar 03, 2021 |
| [4.4.LTS](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.4.LTS) | 4.4-dev-3015 | Mar 03, 2021 |
| Platforms | FFmpegKit Version | FFmpeg Version | Release Date |
| :----: | :----: | :----: | :----: |
| React Native | [4.5.0](https://github.com/tanersener/ffmpeg-kit/releases/tag/react.native.v4.5.0) | 4.5-dev-2008 | Oct 01, 2021 |
| Android<br>Apple | [4.5](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.5) | 4.5-dev-2008 | Sep 18, 2021 |
| Android<br>Apple | [4.5.LTS](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.5.LTS) | 4.5-dev-2008 | Sep 18, 2021 |
| Android<br>Apple | [4.4](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.4) | 4.4-dev-3015 | Mar 03, 2021 |
| Android<br>Apple | [4.4.LTS](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.4.LTS) | 4.4-dev-3015 | Mar 03, 2021 |
### 8. LTS Releases
### 9. LTS Releases
`FFmpegKit` binaries are published in two release variants: `Main Release` and `LTS Release`.
@ -139,11 +146,11 @@ This table shows the differences between two variants.
| tvOS Architectures | arm64<br/>x86-64<br/>arm64-simulator | arm64<br/>x86-64 |
| tvOS Bundle Format | XCFrameworks | Frameworks |
### 9. Documentation
### 10. Documentation
A more detailed documentation is available under [Wiki](https://github.com/tanersener/ffmpeg-kit/wiki).
### 10. Test Applications
### 11. Test Applications
You can see how `FFmpegKit` is used inside an application by running test applications created under
[FFmpegKit Test](https://github.com/tanersener/ffmpeg-kit-test) project.
@ -151,12 +158,12 @@ You can see how `FFmpegKit` is used inside an application by running test applic
All applications are identical and supports command execution, video encoding, accessing https urls, encoding audio,
burning subtitles, video stabilisation, pipe operations and concurrent command execution.
### 11. License
### 12. License
`FFmpegKit` is licensed under the `LGPL v3.0`. However, if source code is built using the optional `--enable-gpl` flag
or prebuilt binaries with `-gpl` postfix are used, then `FFmpegKit` is subject to the `GPL v3.0` license.
### 12. Patents
### 13. Patents
It is not clearly explained in their documentation, but it is believed that `FFmpeg`, `kvazaar`, `x264` and `x265`
include algorithms which are subject to software patents. If you live in a country where software algorithms are
@ -167,15 +174,15 @@ that you seek legal advice first. See [FFmpeg Patent Mini-FAQ](https://ffmpeg.or
distribute that library, then you are subject to pay MPEG LA licensing fees. Refer to
[OpenH264 FAQ](https://www.openh264.org/faq.html) page for the details.
### 13. Contributing
### 14. Contributing
Feel free to submit issues or pull requests.
Please note that `main` includes only the latest released source code. Changes planned for the next release are
developed under the `development` branch. Therefore, if you want to create a pull request, please open it against
the `development`.
Please note that `main` includes only the latest released source code. Changes planned for the next release are
developed under the `development` branches (`development` for native platforms, `development-react-native` for
`react-native`). Therefore, if you want to create a pull request, please open it against them.
### 14. See Also
### 15. See Also
- [FFmpeg API Documentation](https://ffmpeg.org/doxygen/4.0/index.html)
- [FFmpeg Wiki](https://trac.ffmpeg.org/wiki/WikiStart)

View File

@ -7,9 +7,10 @@ FFmpeg Kit for applications.
### 1. Features
- Scripts to build FFmpeg libraries
- `FFmpegKit` wrapper library to run `FFmpeg`/`FFprobe` commands in applications
- Supports Android, iOS, macOS and tvOS
- Supports native platforms: Android, iOS, macOS and tvOS
- Supports hybrid platforms: React Native
- Based on FFmpeg `v4.5-dev` with optional system and external libraries
- 8 prebuilt binary packages available at [Github](https://github.com/tanersener/ffmpeg-kit/releases), [Maven Central](https://search.maven.org) and [CocoaPods](https://cocoapods.org).
- 8 prebuilt binary packages available at [Github](https://github.com/tanersener/ffmpeg-kit/releases), [Maven Central](https://search.maven.org), [CocoaPods](https://cocoapods.org) and [npm](https://www.npmjs.com)
- Licensed under `LGPL 3.0`, or `GPL v3.0` if GPL licensed libraries are enabled
### 2. Android
@ -22,27 +23,32 @@ See [Android](https://github.com/tanersener/ffmpeg-kit/tree/development/android)
See [Apple](https://github.com/tanersener/ffmpeg-kit/tree/development/apple) to use `FFmpegKit` on `Apple` platforms
(`iOS`, `macOS`, `tvOS`).
### 4. Build Scripts
### 4. React Native
Use `android.sh`, `ios.sh`, `macos.sh` and `tvos.sh` to build `FFmpegKit` for each platform.
See [React Native](https://github.com/tanersener/ffmpeg-kit/tree/main/react-native) to learn more about `FFmpegKit` for
`React Native`.
### 5. Build Scripts
Use `android.sh`, `ios.sh`, `macos.sh` and `tvos.sh` to build `FFmpegKit` for each native platform.
All scripts support additional options to enable optional libraries and disable platform architectures.
### 5. FFmpegKit Library
### 6. FFmpegKit Library
`FFmpegKit` is a wrapper library that allows you to easily run `FFmpeg`/`FFprobe` commands in applications. It
provides additional features on top of `FFmpeg` to enable platform specific resources, control how commands are
executed and how the results are handled.
`Android` library has a `Java` API and `Apple` libraries (`iOS`, `macOS`, `tvOS`) have an `Objective-C` API,
which are identical in terms of features and capabilities.
`Android` library of `FFmpegKit` has a `Java` API, `Apple` libraries (`iOS`, `macOS`, `tvOS`) have an `Objective-C`
API and `React Native` library provides a `JavaScript` API, which are identical in terms of features and capabilities.
### 6. Binary Packages
### 7. Packages
There are eight different `ffmpeg-kit` packages distributed on
[Github](https://github.com/tanersener/ffmpeg-kit/releases),
[Maven Central](https://search.maven.org) and [CocoaPods](https://cocoapods.org).
Below you can see which external libraries are enabled in each one of them.
[Maven Central](https://search.maven.org), [CocoaPods](https://cocoapods.org) and [npm](https://www.npmjs.com).
Below you can see which system libraries and external libraries are enabled in each one of them.
Please remember that some parts of `FFmpeg` are licensed under the `GPL` and only `GPL` licensed `ffmpeg-kit` packages
include them.
@ -79,21 +85,22 @@ include them.
- `AVFoundation` is not available on `tvOS`
- `VideoToolbox` is not available on LTS releases of `iOS` and `tvOS`
### 7. Versions
### 8. Versions
`FFmpegKit` binaries generated use the same major and minor version numbers as the upstream `FFmpeg` project.
The exact version number of `FFmpeg` is obtained using `git describe --tags`. `dev` part in the version string
indicates that `FFmpeg` source code is cloned from the `FFmpeg` `master` branch.
| FFmpegKit Version | FFmpeg Version | Release Date |
| :----: | :----: |:----: |
| [4.5](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.5) | 4.5-dev-2008 | Sep 18, 2021 |
| [4.5.LTS](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.5.LTS) | 4.5-dev-2008 | Sep 18, 2021 |
| [4.4](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.4) | 4.4-dev-3015 | Mar 03, 2021 |
| [4.4.LTS](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.4.LTS) | 4.4-dev-3015 | Mar 03, 2021 |
| Platforms | FFmpegKit Version | FFmpeg Version | Release Date |
| :----: | :----: | :----: | :----: |
| React Native | [4.5.0](https://github.com/tanersener/ffmpeg-kit/releases/tag/react.native.v4.5.0) | 4.5-dev-2008 | Oct 01, 2021 |
| Android<br>Apple | [4.5](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.5) | 4.5-dev-2008 | Sep 18, 2021 |
| Android<br>Apple | [4.5.LTS](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.5.LTS) | 4.5-dev-2008 | Sep 18, 2021 |
| Android<br>Apple | [4.4](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.4) | 4.4-dev-3015 | Mar 03, 2021 |
| Android<br>Apple | [4.4.LTS](https://github.com/tanersener/ffmpeg-kit/releases/tag/v4.4.LTS) | 4.4-dev-3015 | Mar 03, 2021 |
### 8. LTS Releases
### 9. LTS Releases
`FFmpegKit` binaries are published in two release variants: `Main Release` and `LTS Release`.
@ -122,11 +129,11 @@ This table shows the differences between two variants.
| tvOS Architectures | arm64<br/>x86-64<br/>arm64-simulator | arm64<br/>x86-64 |
| tvOS Bundle Format | XCFrameworks | Frameworks |
### 9. Documentation
### 10. Documentation
A more detailed documentation is available under [Wiki](https://github.com/tanersener/ffmpeg-kit/wiki).
### 10. Test Applications
### 11. Test Applications
You can see how `FFmpegKit` is used inside an application by running test applications created under
[FFmpegKit Test](https://github.com/tanersener/ffmpeg-kit-test) project.
@ -134,12 +141,12 @@ You can see how `FFmpegKit` is used inside an application by running test applic
All applications are identical and supports command execution, video encoding, accessing https urls, encoding audio,
burning subtitles, video stabilisation, pipe operations, concurrent command execution.
### 11. License
### 12. License
`FFmpegKit` is licensed under the `LGPL v3.0`. However, if source code is built using the optional `--enable-gpl` flag
or prebuilt binaries with `-gpl` postfix are used, then `FFmpegKit` is subject to the `GPL v3.0` license.
### 12. Patents
### 13. Patents
It is not clearly explained in their documentation, but it is believed that `FFmpeg`, `kvazaar`, `x264` and `x265`
include algorithms which are subject to software patents. If you live in a country where software algorithms are
@ -150,15 +157,15 @@ that you seek legal advice first. See [FFmpeg Patent Mini-FAQ](https://ffmpeg.or
distribute that library, then you are subject to pay MPEG LA licensing fees. Refer to
[OpenH264 FAQ](https://www.openh264.org/faq.html) page for the details.
### 13. Contributing
### 14. Contributing
Feel free to submit issues or pull requests.
Please note that `main` includes only the latest released source code. Changes planned for the next release are
developed under the `development` branch. Therefore, if you want to create a pull request, please open it against
the `development`.
developed under the `development` branches (`development` for native platforms, `development-react-native` for
`react-native`). Therefore, if you want to create a pull request, please open it against them.
### 14. See Also
### 15. See Also
- [FFmpeg API Documentation](https://ffmpeg.org/doxygen/4.0/index.html)
- [FFmpeg Wiki](https://trac.ffmpeg.org/wiki/WikiStart)

View File

@ -0,0 +1,15 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

3
react-native/.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
*.pbxproj -text
# specific for windows script files
*.bat text eol=crlf

60
react-native/.gitignore vendored Normal file
View File

@ -0,0 +1,60 @@
# OSX
#
.DS_Store
# XDE
.expo/
# VSCode
.vscode/
jsconfig.json
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IJ
#
.idea
.gradle
local.properties
android.iml
# Cocoapods
#
example/ios/Pods
# node.js
#
node_modules/
npm-debug.log
yarn-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
android/app/libs
android/keystores/debug.keystore
# Expo
.expo/*
# generated by bob
lib/

303
react-native/README.md Normal file
View File

@ -0,0 +1,303 @@
# FFmpegKit for React Native
### 1. Features
- Includes both `FFmpeg` and `FFprobe`
- Supports
- Both `Android` and `iOS`
- FFmpeg `v4.5-dev` releases
- `arm-v7a`, `arm-v7a-neon`, `arm64-v8a`, `x86` and `x86_64` architectures on Android
- `Android API Level 16` or later
- `armv7`, `armv7s`, `arm64`, `arm64-simulator`, `i386`, `x86_64`, `x86_64-mac-catalyst` and `arm64-mac-catalyst` architectures on iOS
- `iOS SDK 9.3` or later
- Can process Storage Access Framework (SAF) Uris on Android
- 24 external libraries
`dav1d`, `fontconfig`, `freetype`, `fribidi`, `gmp`, `gnutls`, `kvazaar`, `lame`, `libass`, `libiconv`, `libilbc`, `libtheora`, `libvorbis`, `libvpx`, `libwebp`, `libxml2`, `opencore-amr`, `opus`, `shine`, `snappy`, `soxr`, `speex`, `twolame`, `vo-amrwbenc`
- 4 external libraries with GPL license
`vid.stab`, `x264`, `x265`, `xvidcore`
- `zlib` and `MediaCodec` Android system libraries
- `bzip2`, `iconv`, `libuuid`, `zlib` system libraries and `AudioToolbox`, `VideoToolbox`, `AVFoundation` system frameworks on iOS
- Includes Typescript definitions
- Licensed under LGPL 3.0, can be customized to support GPL v3.0
### 2. Installation
```sh
yarn add ffmpeg-kit-react-native
```
#### 2.1 Packages
`ffmpeg` includes built-in encoders for some popular formats. However, there are certain external libraries that needs
to be enabled in order to encode specific formats/codecs. For example, to encode an `mp3` file you need `lame` or
`shine` library enabled. You have to install a `ffmpeg-kit-react-native` package that has at least one of them inside.
To encode an `h264` video, you need to install a package with `x264` inside. To encode `vp8` or `vp9` videos, you need
a `ffmpeg-kit-react-native` package with `libvpx` inside.
`ffmpeg-kit` provides eight packages that include different sets of external libraries. These packages are
named according to the external libraries included in them. Refer to
[Packages](https://github.com/tanersener/ffmpeg-kit#7-packages) section of the project README to see the names
of those packages and external libraries included in each of them.
##### 2.1.1 Package Names
The following table shows all package names defined for `ffmpeg-kit-react-native`.
| Package | Main Release | LTS Release |
| :----: | :----: | :----: |
| min | min | min-lts |
| min-gpl | min-gpl | min-gpl-lts |
| https | https | https-lts |
| https-gpl | https-gpl | https-gpl-lts |
| audio | audio | audio-lts |
| video | video | video-lts |
| full | full | full-lts |
| full-gpl | full-gpl | full-gpl-lts |
#### 2.2 Enabling Packages
Installing `ffmpeg-kit-react-native` enables the `https` package by default. It is possible to enable other
packages using the instructions below.
##### 2.2.1 Enabling a Package on Android
- Edit `android/build.gradle` file and add the package name in `ext.ffmpegKitPackage` variable.
```
ext {
ffmpegKitPackage = "<package name>"
}
```
##### 2.2.2 Enabling a Package on iOS
- Edit `ios/Podfile` file and add the package name as `subspec`. After that run `pod install` again.
```
pod 'ffmpeg-kit-react-native', :subspecs => ['<package name>'], :podspec => '../node_modules/ffmpeg-kit-react-native/ffmpeg-kit-react-native.podspec'
```
- Note that if you have `use_native_modules!` in your `Podfile`, specifying a `subspec` may cause the following error.
You can fix it by defining `ffmpeg-kit-react-native` dependency before `use_native_modules!` in your `Podfile`.
```
[!] There are multiple dependencies with different sources for `ffmpeg-kit-react-native` in `Podfile`:
- ffmpeg-kit-react-native (from `../node_modules/ffmpeg-kit-react-native`)
- ffmpeg-kit-react-native/video (from `../node_modules/ffmpeg-kit-react-native/ffmpeg-kit-react-native.podspec`)
```
#### 2.3 Enabling LTS Releases
In order to install the `LTS` variant, install the `https-lts` package using instructions in `2.2` or append `-lts` to
the package name you are using.
#### 2.4 LTS Releases
`ffmpeg-kit-react-native` is published in two different variants: `Main Release` and `LTS Release`. Both releases
share the same source code but is built with different settings (Architectures, API Level, iOS Min SDK, etc.). Refer to
[LTS Releases](https://github.com/tanersener/ffmpeg-kit#9-lts-releases) section of the project README to see how they
compare to each other.
### 3. Using
1. Execute FFmpeg commands.
```js
import { FFmpegKit } from 'ffmpeg-kit-react-native';
FFmpegKit.executeAsync('-i file1.mp4 -c:v mpeg4 file2.mp4', async (session) => {
const returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
// SUCCESS
} else if (ReturnCode.isCancel(returnCode)) {
// CANCEL
} else {
// ERROR
}
});
```
2. Each `execute` call creates a new session. Access every detail about your execution from the
session created.
```js
FFmpegKit.executeAsync('-i file1.mp4 -c:v mpeg4 file2.mp4').then(async (session) => {
// Unique session id created for this execution
const sessionId = session.getSessionId();
// Command arguments as a single string
const command = session.getCommand();
// Command arguments
const commandArguments = session.getArguments();
// State of the execution. Shows whether it is still running or completed
const state = await session.getState();
// Return code for completed sessions. Will be undefined if session is still running or FFmpegKit fails to run it
const returnCode = await session.getReturnCode()
const startTime = session.getStartTime();
const endTime = await session.getEndTime();
const duration = await session.getDuration();
// Console output generated for this execution
const output = await session.getOutput();
// The stack trace if FFmpegKit fails to run a command
const failStackTrace = await session.getFailStackTrace()
// The list of logs generated for this execution
const logs = await session.getLogs();
// The list of statistics generated for this execution (only available on FFmpegSession)
const statistics = await session.getStatistics();
});
```
3. Execute `FFmpeg` commands by providing session specific `execute`/`log`/`session` callbacks.
```js
FFmpegKit.executeAsync('-i file1.mp4 -c:v mpeg4 file2.mp4', session => {
// CALLED WHEN SESSION IS EXECUTED
}, log => {
// CALLED WHEN SESSION PRINTS LOGS
}, statistics => {
// CALLED WHEN SESSION GENERATES STATISTICS
});
```
4. Execute `FFprobe` commands.
```js
FFprobeKit.executeAsync(ffprobeCommand, session => {
// CALLED WHEN SESSION IS EXECUTED
});
```
5. Get media information for a file/url.
```js
FFprobeKit.getMediaInformationAsync('<file path or url>', async (session) => {
const information = await session.getMediaInformation();
});
```
6. Stop ongoing FFmpeg operations.
- Stop all sessions
```js
FFmpegKit.cancel();
```
- Stop a specific session
```js
FFmpegKit.cancel(sessionId);
```
7. (Android) Convert Storage Access Framework (SAF) Uris into paths that can be read or written by
`FFmpegKit` and `FFprobeKit`.
- Reading a file:
```js
FFmpegKitConfig.selectDocumentForRead('*/*').then(uri => {
FFmpegKitConfig.getSafParameterForRead(uri).then(safUrl => {
FFmpegKit.executeAsync(`-i ${safUrl} -c:v mpeg4 file2.mp4`);
});
});
```
- Writing to a file:
```js
FFmpegKitConfig.selectDocumentForWrite('video.mp4', 'video/*').then(uri => {
FFmpegKitConfig.getSafParameterForWrite(uri).then(safUrl => {
FFmpegKit.executeAsync(`-i file1.mp4 -c:v mpeg4 ${safUrl}`);
});
});
```
8. Get previous `FFmpeg` and `FFprobe` sessions from the session history.
```js
FFmpegKit.listSessions().then(sessionList => {
sessionList.forEach(async session => {
const sessionId = session.getSessionId();
});
});
FFprobeKit.listSessions().then(sessionList => {
sessionList.forEach(async session => {
const sessionId = session.getSessionId();
});
});
```
9. Enable global callbacks.
- Execute Callback, called when an async execution is ended
```js
FFmpegKitConfig.enableExecuteCallback(session => {
const sessionId = session.getSessionId();
});
```
- Log Callback, called when a session generates logs
```js
FFmpegKitConfig.enableLogCallback(log => {
const message = log.getMessage();
});
```
- Statistics Callback, called when a session generates statistics
```js
FFmpegKitConfig.enableStatisticsCallback(statistics => {
const size = statistics.getSize();
});
```
10. Register system fonts and custom font directories.
```js
FFmpegKitConfig.setFontDirectoryList(["/system/fonts", "/System/Library/Fonts", "<folder with fonts>"]);
```
### 4. Test Application
You can see how `FFmpegKit` is used inside an application by running `react-native` test applications developed under
the [FFmpegKit Test](https://github.com/tanersener/ffmpeg-kit-test) project.
### 5. Tips
See [Tips](https://github.com/tanersener/ffmpeg-kit/wiki/Tips) wiki page.
### 6. License
See [License](https://github.com/tanersener/ffmpeg-kit/wiki/License) wiki page.
### 7. Patents
See [Patents](https://github.com/tanersener/ffmpeg-kit/wiki/Patents) wiki page.

View File

@ -0,0 +1,126 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.2'
}
}
apply plugin: 'com.android.library'
static def safePackageName(String packageName) {
packageName.replace("-lts", "")
}
def safePackageVersion(String packageName) {
def version = project.properties['ffmpegKit.android.main.version']
def ltsVersion = project.properties['ffmpegKit.android.lts.version']
packageName.contains("-lts") ? ltsVersion + ".LTS" : version
}
def safeExtGet(String prop, String fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion safeExtGet('ffmpegKitPackage', 'https').contains("-lts") ? 16 : 24
targetSdkVersion 30
versionCode 450
versionName "4.5.0"
}
buildTypes {
release {
minifyEnabled false
}
}
lintOptions {
disable 'GradleCompatible'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
mavenCentral()
jcenter()
google()
def found = false
def defaultDir = null
def androidSourcesName = 'React Native sources'
if (rootProject.ext.has('reactNativeAndroidRoot')) {
defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
} else {
defaultDir = new File(
projectDir,
'node_modules/react-native/android'
)
}
if (defaultDir.exists()) {
maven {
url defaultDir.toString()
name androidSourcesName
}
logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
found = true
} else {
def parentDir = rootProject.projectDir
1.upto(5, {
if (found) return true
parentDir = parentDir.parentFile
def androidSourcesDir = new File(
parentDir,
'node_modules/react-native'
)
def androidPrebuiltBinaryDir = new File(
parentDir,
'node_modules/react-native/android'
)
if (androidPrebuiltBinaryDir.exists()) {
maven {
url androidPrebuiltBinaryDir.toString()
name androidSourcesName
}
logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
found = true
} else if (androidSourcesDir.exists()) {
maven {
url androidSourcesDir.toString()
name androidSourcesName
}
logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
found = true
}
})
}
if (!found) {
throw new GradleException(
"${project.name}: unable to locate React Native android sources. " +
"Ensure you have you installed React Native as a dependency in your project and try again."
)
}
}
dependencies {
api 'com.facebook.react:react-native:+'
implementation 'com.arthenica:ffmpeg-kit-' + safePackageName(safeExtGet('ffmpegKitPackage', 'https')) + ':' + safePackageVersion(safeExtGet('ffmpegKitPackage', 'https'))
}

View File

@ -0,0 +1,3 @@
android.useAndroidX=true
ffmpegKit.android.main.version=4.5
ffmpegKit.android.lts.version=4.5

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
react-native/android/gradlew vendored Executable file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
react-native/android/gradlew.bat vendored Normal file
View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arthenica.ffmpegkit.reactnative">
</manifest>

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.ffmpegkit.reactnative;
import static com.arthenica.ffmpegkit.reactnative.FFmpegKitReactNativeModule.LIBRARY_NAME;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import java.io.IOException;
public class AsyncWriteToPipeTask implements Runnable {
private final String inputPath;
private final String namedPipePath;
private final Promise promise;
public AsyncWriteToPipeTask(final String inputPath, final String namedPipePath, final Promise promise) {
this.inputPath = inputPath;
this.namedPipePath = namedPipePath;
this.promise = promise;
}
@Override
public void run() {
int rc;
try {
final String asyncCommand = "cat " + inputPath + " > " + namedPipePath;
Log.d(LIBRARY_NAME, String.format("Starting copy %s to pipe %s operation.", inputPath, namedPipePath));
final long startTime = System.currentTimeMillis();
final Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", asyncCommand});
rc = process.waitFor();
final long endTime = System.currentTimeMillis();
Log.d(LIBRARY_NAME, String.format("Copying %s to pipe %s operation completed with rc %d in %d seconds.", inputPath, namedPipePath, rc, (endTime - startTime) / 1000));
promise.resolve(rc);
} catch (final IOException | InterruptedException e) {
Log.e(LIBRARY_NAME, String.format("Copy %s to pipe %s failed with error.", inputPath, namedPipePath), e);
promise.reject("Copy failed", String.format("Copy %s to pipe %s failed with error.", inputPath, namedPipePath), e);
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.ffmpegkit.reactnative;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Collections;
import java.util.List;
public class FFmpegKitReactNativePackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
return Collections.singletonList(new FFmpegKitReactNativeModule(reactContext));
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,3 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
};

View File

@ -0,0 +1,135 @@
require "json"
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
Pod::Spec.new do |s|
s.name = package["name"]
s.version = package["version"]
s.summary = package["description"]
s.homepage = package["homepage"]
s.license = package["license"]
s.authors = package["author"]
s.platform = :ios
s.requires_arc = true
s.static_framework = true
s.source = { :git => "https://github.com/tanersener/ffmpeg-kit.git", :tag => "react.native.v#{s.version}" }
s.default_subspec = 'https'
s.dependency "React-Core"
s.subspec 'min' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-min', "4.5"
ss.ios.deployment_target = '12.1'
end
s.subspec 'min-lts' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-min', "4.5.LTS"
ss.ios.deployment_target = '9.3'
end
s.subspec 'min-gpl' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-min-gpl', "4.5"
ss.ios.deployment_target = '12.1'
end
s.subspec 'min-gpl-lts' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-min-gpl', "4.5.LTS"
ss.ios.deployment_target = '9.3'
end
s.subspec 'https' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-https', "4.5"
ss.ios.deployment_target = '12.1'
end
s.subspec 'https-lts' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-https', "4.5.LTS"
ss.ios.deployment_target = '9.3'
end
s.subspec 'https-gpl' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-https-gpl', "4.5"
ss.ios.deployment_target = '12.1'
end
s.subspec 'https-gpl-lts' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-https-gpl', "4.5.LTS"
ss.ios.deployment_target = '9.3'
end
s.subspec 'audio' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-audio', "4.5"
ss.ios.deployment_target = '12.1'
end
s.subspec 'audio-lts' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-audio', "4.5.LTS"
ss.ios.deployment_target = '9.3'
end
s.subspec 'video' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-video', "4.5"
ss.ios.deployment_target = '12.1'
end
s.subspec 'video-lts' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-video', "4.5.LTS"
ss.ios.deployment_target = '9.3'
end
s.subspec 'full' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-full', "4.5"
ss.ios.deployment_target = '12.1'
end
s.subspec 'full-lts' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-full', "4.5.LTS"
ss.ios.deployment_target = '9.3'
end
s.subspec 'full-gpl' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-full-gpl', "4.5"
ss.ios.deployment_target = '12.1'
end
s.subspec 'full-gpl-lts' do |ss|
ss.source_files = '**/FFmpegKitReactNativeModule.m',
'**/FFmpegKitReactNativeModule.h'
ss.dependency 'ffmpeg-kit-ios-full-gpl', "4.5.LTS"
ss.ios.deployment_target = '9.3'
end
end

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Foundation/Foundation.h>
#import <ffmpegkit/FFmpegKitConfig.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface FFmpegKitReactNativeModule : RCTEventEmitter<RCTBridgeModule>
@end

View File

@ -0,0 +1,779 @@
/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#import "FFmpegKitReactNativeModule.h"
#import <React/RCTLog.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import <ffmpegkit/FFmpegKit.h>
#import <ffmpegkit/FFprobeKit.h>
#import <ffmpegkit/ArchDetect.h>
#import <ffmpegkit/MediaInformation.h>
#import <ffmpegkit/Packages.h>
static NSString *const PLATFORM_NAME = @"ios";
// LOG CLASS
static NSString *const KEY_LOG_SESSION_ID = @"sessionId";
static NSString *const KEY_LOG_LEVEL = @"level";
static NSString *const KEY_LOG_MESSAGE = @"message";
// STATISTICS CLASS
static NSString *const KEY_STATISTICS_SESSION_ID = @"sessionId";
static NSString *const KEY_STATISTICS_VIDEO_FRAME_NUMBER = @"videoFrameNumber";
static NSString *const KEY_STATISTICS_VIDEO_FPS = @"videoFps";
static NSString *const KEY_STATISTICS_VIDEO_QUALITY = @"videoQuality";
static NSString *const KEY_STATISTICS_SIZE = @"size";
static NSString *const KEY_STATISTICS_TIME = @"time";
static NSString *const KEY_STATISTICS_BITRATE = @"bitrate";
static NSString *const KEY_STATISTICS_SPEED = @"speed";
// SESSION CLASS
static NSString *const KEY_SESSION_ID = @"sessionId";
static NSString *const KEY_SESSION_CREATE_TIME = @"createTime";
static NSString *const KEY_SESSION_START_TIME = @"startTime";
static NSString *const KEY_SESSION_COMMAND = @"command";
static NSString *const KEY_SESSION_TYPE = @"type";
static NSString *const KEY_SESSION_MEDIA_INFORMATION = @"mediaInformation";
// SESSION TYPE
static int const SESSION_TYPE_FFMPEG = 1;
static int const SESSION_TYPE_FFPROBE = 2;
static int const SESSION_TYPE_MEDIA_INFORMATION = 3;
// EVENTS
static NSString *const EVENT_LOG_CALLBACK_EVENT = @"FFmpegKitLogCallbackEvent";
static NSString *const EVENT_STATISTICS_CALLBACK_EVENT = @"FFmpegKitStatisticsCallbackEvent";
static NSString *const EVENT_EXECUTE_CALLBACK_EVENT = @"FFmpegKitExecuteCallbackEvent";
extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
@implementation FFmpegKitReactNativeModule {
BOOL logsEnabled;
BOOL statisticsEnabled;
dispatch_queue_t asyncWriteToPipeDispatchQueue;
}
RCT_EXPORT_MODULE(FFmpegKitReactNativeModule);
- (instancetype)init {
self = [super init];
if (self) {
logsEnabled = false;
statisticsEnabled = false;
asyncWriteToPipeDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self registerGlobalCallbacks];
}
return self;
}
- (NSArray<NSString*>*)supportedEvents {
NSMutableArray *array = [NSMutableArray array];
[array addObject:EVENT_LOG_CALLBACK_EVENT];
[array addObject:EVENT_STATISTICS_CALLBACK_EVENT];
[array addObject:EVENT_EXECUTE_CALLBACK_EVENT];
return array;
}
- (void)registerGlobalCallbacks {
[FFmpegKitConfig enableExecuteCallback:^(id<Session> session){
NSDictionary *dictionary = [FFmpegKitReactNativeModule toSessionDictionary:session];
[self sendEventWithName:EVENT_EXECUTE_CALLBACK_EVENT body:dictionary];
}];
[FFmpegKitConfig enableLogCallback: ^(Log* log){
if (self->logsEnabled) {
NSDictionary *dictionary = [FFmpegKitReactNativeModule toLogDictionary:log];
[self sendEventWithName:EVENT_LOG_CALLBACK_EVENT body:dictionary];
}
}];
[FFmpegKitConfig enableStatisticsCallback:^(Statistics* statistics){
if (self->statisticsEnabled) {
NSDictionary *dictionary = [FFmpegKitReactNativeModule toStatisticsDictionary:statistics];
[self sendEventWithName:EVENT_STATISTICS_CALLBACK_EVENT body:dictionary];
}
}];
}
// AbstractSession
RCT_EXPORT_METHOD(abstractSessionGetEndTime:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
NSDate* endTime = [session getEndTime];
if (endTime == nil) {
resolve(nil);
} else {
resolve([NSNumber numberWithDouble:[endTime timeIntervalSince1970]*1000]);
}
}
}
RCT_EXPORT_METHOD(abstractSessionGetDuration:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
resolve([NSNumber numberWithLong:[session getDuration]]);
}
}
RCT_EXPORT_METHOD(abstractSessionGetAllLogs:(int)sessionId withTimeout:(int)waitTimeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
int timeout;
if ([FFmpegKitReactNativeModule isValidPositiveNumber:waitTimeout]) {
timeout = waitTimeout;
} else {
timeout = AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
}
NSArray* allLogs = [session getAllLogsWithTimeout:timeout];
resolve([FFmpegKitReactNativeModule toLogArray:allLogs]);
}
}
RCT_EXPORT_METHOD(abstractSessionGetLogs:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
NSArray* logs = [session getLogs];
resolve([FFmpegKitReactNativeModule toLogArray:logs]);
}
}
RCT_EXPORT_METHOD(abstractSessionGetAllLogsAsString:(int)sessionId withTimeout:(int)waitTimeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
int timeout;
if ([FFmpegKitReactNativeModule isValidPositiveNumber:waitTimeout]) {
timeout = waitTimeout;
} else {
timeout = AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
}
NSString* allLogsAsString = [session getAllLogsAsStringWithTimeout:timeout];
resolve(allLogsAsString);
}
}
RCT_EXPORT_METHOD(abstractSessionGetState:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
resolve([FFmpegKitReactNativeModule sessionStateToNumber:[session getState]]);
}
}
RCT_EXPORT_METHOD(abstractSessionGetReturnCode:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
ReturnCode* returnCode = [session getReturnCode];
if (returnCode == nil) {
resolve(nil);
} else {
resolve([NSNumber numberWithInt:[returnCode getValue]]);
}
}
}
RCT_EXPORT_METHOD(abstractSessionGetFailStackTrace:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
resolve([session getFailStackTrace]);
}
}
RCT_EXPORT_METHOD(thereAreAsynchronousMessagesInTransmit:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
resolve([NSNumber numberWithBool:[session thereAreAsynchronousMessagesInTransmit]]);
}
}
// ArchDetect
RCT_EXPORT_METHOD(getArch:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([ArchDetect getArch]);
}
// FFmpegSession
RCT_EXPORT_METHOD(ffmpegSession:(NSArray*)arguments resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
FFmpegSession* session = [[FFmpegSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs];
resolve([FFmpegKitReactNativeModule toSessionDictionary:session]);
}
RCT_EXPORT_METHOD(ffmpegSessionGetAllStatistics:(int)sessionId withTimeout:(int)waitTimeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
if ([session isMemberOfClass:[FFmpegSession class]]) {
int timeout;
if ([FFmpegKitReactNativeModule isValidPositiveNumber:waitTimeout]) {
timeout = waitTimeout;
} else {
timeout = AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
}
NSArray* allStatistics = [(FFmpegSession*)session getAllStatisticsWithTimeout:timeout];
resolve([FFmpegKitReactNativeModule toStatisticsArray:allStatistics]);
} else {
reject(@"NOT_FFMPEG_SESSION", @"A session is found but it does not have the correct type.", nil);
}
}
}
RCT_EXPORT_METHOD(ffmpegSessionGetStatistics:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
if ([session isMemberOfClass:[FFmpegSession class]]) {
NSArray* statistics = [(FFmpegSession*)session getStatistics];
resolve([FFmpegKitReactNativeModule toStatisticsArray:statistics]);
} else {
reject(@"NOT_FFMPEG_SESSION", @"A session is found but it does not have the correct type.", nil);
}
}
}
// FFprobeSession
RCT_EXPORT_METHOD(ffprobeSession:(NSArray*)arguments resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
FFprobeSession* session = [[FFprobeSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs];
resolve([FFmpegKitReactNativeModule toSessionDictionary:session]);
}
// MediaInformationSession
RCT_EXPORT_METHOD(mediaInformationSession:(NSArray*)arguments resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
MediaInformationSession* session = [[MediaInformationSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil];
resolve([FFmpegKitReactNativeModule toSessionDictionary:session]);
}
// MediaInformationJsonParser
RCT_EXPORT_METHOD(mediaInformationJsonParserFrom:(NSString*)ffprobeJsonOutput resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
NSError *error;
MediaInformation* mediaInformation = [MediaInformationJsonParser from:ffprobeJsonOutput with:error];
if (error == nil) {
resolve([FFmpegKitReactNativeModule toMediaInformationDictionary:mediaInformation]);
} else {
NSLog(@"MediaInformation parsing failed: %@.\n", error);
resolve(nil);
}
}
RCT_EXPORT_METHOD(mediaInformationJsonParserFromWithError:(NSString*)ffprobeJsonOutput resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
NSError *error;
MediaInformation* mediaInformation = [MediaInformationJsonParser from:ffprobeJsonOutput with:error];
if (error == nil) {
resolve([FFmpegKitReactNativeModule toMediaInformationDictionary:mediaInformation]);
} else {
NSLog(@"MediaInformation parsing failed: %@.\n", error);
reject(@"PARSE_FAILED", @"Parsing MediaInformation failed with JSON error.", nil);
}
}
// FFmpegKitConfig
RCT_EXPORT_METHOD(enableRedirection:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[self enableLogs];
[self enableStatistics];
[FFmpegKitConfig enableRedirection];
resolve(nil);
}
RCT_EXPORT_METHOD(disableRedirection:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig disableRedirection];
resolve(nil);
}
RCT_EXPORT_METHOD(enableLogs:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[self enableLogs];
resolve(nil);
}
RCT_EXPORT_METHOD(disableLogs:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[self disableLogs];
resolve(nil);
}
RCT_EXPORT_METHOD(enableStatistics:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[self enableStatistics];
resolve(nil);
}
RCT_EXPORT_METHOD(disableStatistics:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[self disableStatistics];
resolve(nil);
}
RCT_EXPORT_METHOD(setFontconfigConfigurationPath:(NSString*)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig setFontconfigConfigurationPath:path];
resolve(nil);
}
RCT_EXPORT_METHOD(setFontDirectory:(NSString*)fontDirectoryPath with:(NSDictionary*)fontNameMap resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig setFontDirectory:fontDirectoryPath with:fontNameMap];
resolve(nil);
}
RCT_EXPORT_METHOD(setFontDirectoryList:(NSArray*)fontDirectoryList with:(NSDictionary*)fontNameMap resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig setFontDirectoryList:fontDirectoryList with:fontNameMap];
resolve(nil);
}
RCT_EXPORT_METHOD(registerNewFFmpegPipe:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitConfig registerNewFFmpegPipe]);
}
RCT_EXPORT_METHOD(closeFFmpegPipe:(NSString*)ffmpegPipePath resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig closeFFmpegPipe:ffmpegPipePath];
resolve(nil);
}
RCT_EXPORT_METHOD(getFFmpegVersion:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitConfig getFFmpegVersion]);
}
RCT_EXPORT_METHOD(isLTSBuild:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([NSNumber numberWithInt:[FFmpegKitConfig isLTSBuild]]);
}
RCT_EXPORT_METHOD(getBuildDate:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitConfig getBuildDate]);
}
RCT_EXPORT_METHOD(setEnvironmentVariable:(NSString*)variableName with:(NSString*)variableValue resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig setEnvironmentVariable:variableName value:variableValue];
resolve(nil);
}
RCT_EXPORT_METHOD(ignoreSignal:(int)signalValue resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
if ((signalValue == SignalInt) || (signalValue == SignalQuit) || (signalValue == SignalPipe) || (signalValue == SignalTerm) || (signalValue == SignalXcpu)) {
resolve(nil);
} else {
reject(@"INVALID_SIGNAL", @"Signal value not supported.", nil);
}
}
RCT_EXPORT_METHOD(asyncFFmpegSessionExecute:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
if ([session isMemberOfClass:[FFmpegSession class]]) {
[FFmpegKitConfig asyncFFmpegExecute:(FFmpegSession*)session];
resolve(nil);
} else {
reject(@"NOT_FFMPEG_SESSION", @"A session is found but it does not have the correct type.", nil);
}
}
}
RCT_EXPORT_METHOD(asyncFFprobeSessionExecute:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
if ([session isMemberOfClass:[FFprobeSession class]]) {
[FFmpegKitConfig asyncFFprobeExecute:(FFprobeSession*)session];
resolve(nil);
} else {
reject(@"NOT_FFPROBE_SESSION", @"A session is found but it does not have the correct type.", nil);
}
}
}
RCT_EXPORT_METHOD(asyncMediaInformationSessionExecute:(int)sessionId withTimeout:(int)waitTimeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
if ([session isMemberOfClass:[MediaInformationSession class]]) {
int timeout;
if ([FFmpegKitReactNativeModule isValidPositiveNumber:waitTimeout]) {
timeout = waitTimeout;
} else {
timeout = AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
}
[FFmpegKitConfig asyncGetMediaInformationExecute:(MediaInformationSession*)session withTimeout:timeout];
resolve(nil);
} else {
reject(@"NOT_MEDIA_INFORMATION_SESSION", @"A session is found but it does not have the correct type.", nil);
}
}
}
RCT_EXPORT_METHOD(getLogLevel:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([NSNumber numberWithInt:[FFmpegKitConfig getLogLevel]]);
}
RCT_EXPORT_METHOD(setLogLevel:(int)level resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig setLogLevel:level];
resolve(nil);
}
RCT_EXPORT_METHOD(getSessionHistorySize:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([NSNumber numberWithInt:[FFmpegKitConfig getSessionHistorySize]]);
}
RCT_EXPORT_METHOD(setSessionHistorySize:(int)sessionHistorySize resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig setSessionHistorySize:sessionHistorySize];
resolve(nil);
}
RCT_EXPORT_METHOD(getSession:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
resolve([FFmpegKitReactNativeModule toSessionDictionary:session]);
}
}
RCT_EXPORT_METHOD(getLastSession:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitReactNativeModule toSessionDictionary:[FFmpegKitConfig getLastSession]]);
}
RCT_EXPORT_METHOD(getLastCompletedSession:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitReactNativeModule toSessionDictionary:[FFmpegKitConfig getLastCompletedSession]]);
}
RCT_EXPORT_METHOD(getSessions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitReactNativeModule toSessionArray:[FFmpegKitConfig getSessions]]);
}
RCT_EXPORT_METHOD(clearSessions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig clearSessions];
resolve(nil);
}
RCT_EXPORT_METHOD(getSessionsByState:(int)sessionState resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitReactNativeModule toSessionArray:[FFmpegKitConfig getSessionsByState:sessionState]]);
}
RCT_EXPORT_METHOD(getLogRedirectionStrategy:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitReactNativeModule logRedirectionStrategyToNumber:[FFmpegKitConfig getLogRedirectionStrategy]]);
}
RCT_EXPORT_METHOD(setLogRedirectionStrategy:(int)logRedirectionStrategy resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKitConfig setLogRedirectionStrategy:logRedirectionStrategy];
resolve(nil);
}
RCT_EXPORT_METHOD(messagesInTransmit:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([NSNumber numberWithInt:[FFmpegKitConfig messagesInTransmit:sessionId]]);
}
RCT_EXPORT_METHOD(getPlatform:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve(PLATFORM_NAME);
}
RCT_EXPORT_METHOD(writeToPipe:(NSString*)inputPath onPipe:(NSString*)namedPipePath resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
dispatch_async(asyncWriteToPipeDispatchQueue, ^{
NSLog(@"Starting copy %@ to pipe %@ operation.\n", inputPath, namedPipePath);
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath: inputPath];
if (fileHandle == nil) {
NSLog(@"Failed to open file %@.\n", inputPath);
reject(@"Copy failed", [NSString stringWithFormat:@"Failed to open file %@.", inputPath], nil);
return;
}
NSFileHandle *pipeHandle = [NSFileHandle fileHandleForWritingAtPath: namedPipePath];
if (pipeHandle == nil) {
NSLog(@"Failed to open pipe %@.\n", namedPipePath);
reject(@"Copy failed", [NSString stringWithFormat:@"Failed to open pipe %@.", namedPipePath], nil);
[fileHandle closeFile];
return;
}
int BUFFER_SIZE = 4096;
unsigned long readBytes = 0;
unsigned long totalBytes = 0;
double startTime = CACurrentMediaTime();
@try {
[fileHandle seekToFileOffset: 0];
do {
NSData *data = [fileHandle readDataOfLength:BUFFER_SIZE];
readBytes = [data length];
if (readBytes > 0) {
totalBytes += readBytes;
[pipeHandle writeData:data];
}
} while (readBytes > 0);
double endTime = CACurrentMediaTime();
NSLog(@"Copying %@ to pipe %@ operation completed successfully. %lu bytes copied in %f seconds.\n", inputPath, namedPipePath, totalBytes, (endTime - startTime)/1000);
resolve(0);
} @catch (NSException *e) {
NSLog(@"Copy failed %@.\n", [e reason]);
reject(@"Copy failed", [NSString stringWithFormat:@"Copy %@ to %@ failed with error %@.", inputPath, namedPipePath, [e reason]], nil);
} @finally {
[fileHandle closeFile];
[pipeHandle closeFile];
}
});
}
RCT_EXPORT_METHOD(selectDocument:(BOOL)writable title:(NSString*)title type:(NSString*)type array:(NSArray*)extraTypes resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
reject(@"Not Supported", @"Not supported on iOS platform.", nil);
}
RCT_EXPORT_METHOD(getSafParameter:(BOOL)writable uri:(NSString*)uriString resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
reject(@"Not Supported", @"Not supported on iOS platform.", nil);
}
// FFmpegKit
RCT_EXPORT_METHOD(cancel:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKit cancel];
resolve(nil);
}
RCT_EXPORT_METHOD(cancelSession:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[FFmpegKit cancel:sessionId];
resolve(nil);
}
RCT_EXPORT_METHOD(getFFmpegSessions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitReactNativeModule toSessionArray:[FFmpegKit listSessions]]);
}
// FFprobeKit
RCT_EXPORT_METHOD(getFFprobeSessions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([FFmpegKitReactNativeModule toSessionArray:[FFprobeKit listSessions]]);
}
// Packages
RCT_EXPORT_METHOD(getPackageName:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([Packages getPackageName]);
}
RCT_EXPORT_METHOD(getExternalLibraries:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
resolve([Packages getExternalLibraries]);
}
- (void)enableLogs {
logsEnabled = true;
}
- (void)disableLogs {
logsEnabled = false;
}
- (void)enableStatistics {
statisticsEnabled = true;
}
- (void)disableStatistics {
statisticsEnabled = false;
}
+ (NSDictionary*)toSessionDictionary:(id<Session>) session {
if (session != nil) {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[KEY_SESSION_ID] = [NSNumber numberWithLong: [session getSessionId]];
dictionary[KEY_SESSION_CREATE_TIME] = [NSNumber numberWithDouble:[[session getCreateTime] timeIntervalSince1970]*1000];
dictionary[KEY_SESSION_START_TIME] = [NSNumber numberWithDouble:[[session getStartTime] timeIntervalSince1970]*1000];
dictionary[KEY_SESSION_COMMAND] = [session getCommand];
if ([session isFFprobe]) {
if ([(AbstractSession*)session isMemberOfClass:[MediaInformationSession class]]) {
MediaInformationSession *mediaInformationSession = (MediaInformationSession*)session;
dictionary[KEY_SESSION_MEDIA_INFORMATION] = [FFmpegKitReactNativeModule toMediaInformationDictionary:[mediaInformationSession getMediaInformation]];
dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_MEDIA_INFORMATION];
} else {
dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_FFPROBE];
}
} else {
dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_FFMPEG];
}
return dictionary;
} else {
return nil;
}
}
+ (NSDictionary*)toLogDictionary:(Log*)log {
if (log != nil) {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[KEY_LOG_SESSION_ID] = [NSNumber numberWithLong: [log getSessionId]];
dictionary[KEY_LOG_LEVEL] = [NSNumber numberWithInt: [log getLevel]];
dictionary[KEY_LOG_MESSAGE] = [log getMessage];
return dictionary;
} else {
return nil;
}
}
+ (NSDictionary*)toStatisticsDictionary:(Statistics*)statistics {
if (statistics != nil) {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[KEY_STATISTICS_SESSION_ID] = [NSNumber numberWithLong: [statistics getSessionId]];
dictionary[KEY_STATISTICS_VIDEO_FRAME_NUMBER] = [NSNumber numberWithInt: [statistics getVideoFrameNumber]];
dictionary[KEY_STATISTICS_VIDEO_FPS] = [NSNumber numberWithFloat: [statistics getVideoFps]];
dictionary[KEY_STATISTICS_VIDEO_QUALITY] = [NSNumber numberWithFloat: [statistics getVideoQuality]];
dictionary[KEY_STATISTICS_SIZE] = [NSNumber numberWithLong: [statistics getSize]];
dictionary[KEY_STATISTICS_TIME] = [NSNumber numberWithInt: [statistics getTime]];
dictionary[KEY_STATISTICS_BITRATE] = [NSNumber numberWithDouble: [statistics getBitrate]];
dictionary[KEY_STATISTICS_SPEED] = [NSNumber numberWithDouble: [statistics getSpeed]];
return dictionary;
} else {
return nil;
}
}
+ (NSDictionary*)toMediaInformationDictionary:(MediaInformation*)mediaInformation {
if (mediaInformation != nil) {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
NSDictionary* allProperties = [mediaInformation getAllProperties];
if (allProperties != nil) {
for(NSString *key in [allProperties allKeys]) {
dictionary[key] = [allProperties objectForKey:key];
}
}
return dictionary;
} else {
return nil;
}
}
+ (NSArray*)toLogArray:(NSArray*)logs {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < [logs count]; i++) {
Log* log = [logs objectAtIndex:i];
[array addObject: [FFmpegKitReactNativeModule toLogDictionary:log]];
}
return array;
}
+ (NSArray*)toStatisticsArray:(NSArray*)statisticsArray {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < [statisticsArray count]; i++) {
Statistics* statistics = [statisticsArray objectAtIndex:i];
[array addObject: [FFmpegKitReactNativeModule toStatisticsDictionary:statistics]];
}
return array;
}
+ (NSArray*)toSessionArray:(NSArray*)sessions {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < [sessions count]; i++) {
AbstractSession* session = (AbstractSession*)[sessions objectAtIndex:i];
[array addObject: [FFmpegKitReactNativeModule toSessionDictionary:session]];
}
return array;
}
+ (NSNumber*)sessionStateToNumber:(SessionState)sessionState {
switch (sessionState) {
case SessionStateCreated:
return [NSNumber numberWithInt:0];
case SessionStateRunning:
return [NSNumber numberWithInt:1];
case SessionStateFailed:
return [NSNumber numberWithInt:2];
case SessionStateCompleted:
default:
return [NSNumber numberWithInt:3];
}
}
+ (NSNumber*)logRedirectionStrategyToNumber:(LogRedirectionStrategy)logRedirectionStrategy {
switch (logRedirectionStrategy) {
case LogRedirectionStrategyAlwaysPrintLogs:
return [NSNumber numberWithInt:0];
case LogRedirectionStrategyPrintLogsWhenNoCallbacksDefined:
return [NSNumber numberWithInt:1];
case LogRedirectionStrategyPrintLogsWhenGlobalCallbackNotDefined:
return [NSNumber numberWithInt:2];
case LogRedirectionStrategyPrintLogsWhenSessionCallbackNotDefined:
return [NSNumber numberWithInt:3];
case LogRedirectionStrategyNeverPrintLogs:
default:
return [NSNumber numberWithInt:4];
}
}
+ (BOOL)isValidPositiveNumber:(int)value {
if (value >= 0) {
return true;
} else {
return false;
}
}
@end

View File

@ -0,0 +1,293 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
5E555C0D2413F4C50049A1A2 /* FFmpegKitReactNativeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* FFmpegKitReactNativeModule.m */; };
F4FF95D7245B92E800C19C63 /* FFmpegKitReactNativeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* FFmpegKitReactNativeModule.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libFFmpegKitReactNativeModule.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFFmpegKitReactNativeModule.a; sourceTree = BUILT_PRODUCTS_DIR; };
B3E7B5891CC2AC0600A0062D /* FFmpegKitReactNativeModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FFmpegKitReactNativeModule.m; sourceTree = "<group>"; };
F4FF95D5245B92E700C19C63 /* FFmpegKitReactNativeModule-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FFmpegKitReactNativeModule-Bridging-Header.h"; sourceTree = "<group>"; };
F4FF95D6245B92E800C19C63 /* FFmpegKitReactNativeModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FFmpegKitReactNativeModule.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libFFmpegKitReactNativeModule.a */,
);
name = Products;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
F4FF95D6245B92E800C19C63 /* FFmpegKitReactNativeModule.swift */,
B3E7B5891CC2AC0600A0062D /* FFmpegKitReactNativeModule.m */,
F4FF95D5245B92E700C19C63 /* FFmpegKitReactNativeModule-Bridging-Header.h */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* FFmpegKitReactNativeModule */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "FFmpegKitReactNativeModule" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = FFmpegKitReactNativeModule;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libFFmpegKitReactNativeModule.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0920;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "FFmpegKitReactNativeModule" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* FFmpegKitReactNativeModule */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F4FF95D7245B92E800C19C63 /* FFmpegKitReactNativeModule.swift in Sources */,
B3E7B58A1CC2AC0600A0062D /* FFmpegKitReactNativeModule.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = FFmpegKitReactNativeModule;
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "FFmpegKitReactNativeModule-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = FFmpegKitReactNativeModule;
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "FFmpegKitReactNativeModule-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "FFmpegKitReactNativeModule" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "FFmpegKitReactNativeModule" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}

118
react-native/package.json Normal file
View File

@ -0,0 +1,118 @@
{
"name": "ffmpeg-kit-react-native",
"version": "4.5.0",
"description": "FFmpeg Kit for React Native",
"main": "src/index",
"types": "src/index.d.ts",
"react-native": "src/index",
"source": "src/index",
"files": [
"android",
"ffmpeg-kit-react-native.podspec",
"ios",
"src",
"!android/build",
"!ios/build",
"!**/__tests__",
"!**/__fixtures__",
"!**/__mocks__"
],
"scripts": {
"test": "jest",
"lint": "eslint \"**/*.{js,ts,tsx}\"",
"release": "release-it"
},
"keywords": [
"react-native",
"android",
"ffmpeg",
"ffmpeg-kit",
"ios"
],
"repository": "https://github.com/tanersener/ffmpeg-kit",
"author": "Taner Sener <tanersener@gmail.com> (https://github.com/tanersener)",
"license": "LGPL-3.0",
"bugs": {
"url": "https://github.com/tanersener/ffmpeg-kit/issues"
},
"homepage": "https://github.com/tanersener/ffmpeg-kit",
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
"devDependencies": {
"@commitlint/config-conventional": "^11.0.0",
"@react-native-community/eslint-config": "^2.0.0",
"@release-it/conventional-changelog": "^2.0.0",
"@types/jest": "^26.0.0",
"@types/react": "^16.9.19",
"@types/react-native": "^0.62.13",
"commitlint": "^11.0.0",
"eslint": "^7.2.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-prettier": "^3.1.3",
"jest": "^26.0.1",
"pod-install": "^0.1.0",
"prettier": "^2.0.5",
"react": "^16.13.1",
"react-native": "^0.63.4",
"release-it": "^14.2.2"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
},
"jest": {
"preset": "react-native"
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"release-it": {
"git": {
"commitMessage": "chore: release ${version}",
"tagName": "v${version}"
},
"npm": {
"publish": true
},
"github": {
"release": true
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular"
}
}
},
"eslintConfig": {
"root": true,
"extends": [
"@react-native-community",
"prettier"
],
"rules": {
"prettier/prettier": [
"error",
{
"quoteProps": "consistent",
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false
}
]
}
},
"eslintIgnore": [
"node_modules/"
],
"prettier": {
"quoteProps": "consistent",
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false
}
}

538
react-native/src/index.d.ts vendored Normal file
View File

@ -0,0 +1,538 @@
declare module 'ffmpeg-kit-react-native' {
export abstract class AbstractSession implements Session {
protected constructor();
static createFFmpegSession(argumentsArray: Array<string>, logRedirectionStrategy?: LogRedirectionStrategy): Promise<FFmpegSession>;
static createFFmpegSessionFromMap(sessionMap: { [key: string]: any }): FFmpegSession;
static createFFprobeSession(argumentsArray: Array<string>, logRedirectionStrategy?: LogRedirectionStrategy): Promise<FFprobeSession>;
static createFFprobeSessionFromMap(sessionMap: { [key: string]: any }): FFprobeSession;
static createMediaInformationSession(argumentsArray: Array<string>): Promise<MediaInformationSession>;
static createMediaInformationSessionFromMap(sessionMap: { [key: string]: any }): MediaInformationSession;
getExecuteCallback(): ExecuteCallback;
getLogCallback(): LogCallback;
getSessionId(): number;
getCreateTime(): Date;
getStartTime(): Date;
getEndTime(): Promise<Date>;
getDuration(): Promise<number>;
getArguments(): Array<string>;
getCommand(): string;
getAllLogs(waitTimeout ?: number): Promise<Array<Log>>;
getLogs(): Promise<Array<Log>>;
getAllLogsAsString(waitTimeout?: number): Promise<string>;
getLogsAsString(): Promise<string>;
getOutput(): Promise<string>;
getState(): Promise<SessionState>;
getReturnCode(): Promise<ReturnCode>;
getFailStackTrace(): Promise<string>;
getLogRedirectionStrategy(): LogRedirectionStrategy;
thereAreAsynchronousMessagesInTransmit(): Promise<boolean>;
isFFmpeg(): boolean;
isFFprobe(): boolean;
cancel(): Promise<void>;
}
export class ArchDetect {
static getArch(): Promise<string>;
}
export type ExecuteCallback = (session: Session) => void;
export class FFmpegKit {
static executeAsync(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, statisticsCallback?: StatisticsCallback): Promise<FFmpegSession>;
static executeWithArgumentsAsync(commandArguments: string[], executeCallback?: ExecuteCallback, logCallback?: LogCallback, statisticsCallback?: StatisticsCallback): Promise<FFmpegSession>;
static cancel(sessionId?: number): Promise<void>;
static listSessions(): Promise<FFmpegSession[]>;
}
export class FFmpegKitConfig {
static init(): Promise<void>;
static enableRedirection(): Promise<void>;
static disableRedirection(): Promise<void>;
static setFontconfigConfigurationPath(path: string): Promise<void>;
static setFontDirectory(path: string, mapping?: { [key: string]: string }): Promise<void>;
static setFontDirectoryList(fontDirectoryList: string[], mapping?: { [key: string]: string }): Promise<void>;
static registerNewFFmpegPipe(): Promise<string>;
static closeFFmpegPipe(ffmpegPipePath: string): Promise<void>;
static getFFmpegVersion(): Promise<string>;
static getVersion(): Promise<string>;
static isLTSBuild(): Promise<boolean>;
static getBuildDate(): Promise<string>;
static setEnvironmentVariable(name: string, value: string): Promise<void>;
static ignoreSignal(signal: Signal): Promise<void>;
static asyncFFmpegExecute(session: FFmpegSession): Promise<void>;
static asyncFFprobeExecute(session: FFprobeSession): Promise<void>;
static asyncGetMediaInformationExecute(session: MediaInformationSession, waitTimeout?: number): Promise<void>;
static enableLogCallback(logCallback: LogCallback): void;
static enableStatisticsCallback(statisticsCallback: StatisticsCallback): void;
static enableExecuteCallback(executeCallback: ExecuteCallback): void;
static getLogLevel(): Level;
static setLogLevel(level: Level): Promise<void>;
static getSessionHistorySize(): Promise<number>;
static setSessionHistorySize(sessionHistorySize: number): Promise<void>;
static getSession(sessionId: number): Promise<Session>;
static getLastSession(): Promise<Session>;
static getLastCompletedSession(): Promise<Session>;
static getSessions(): Promise<Session[]>;
static clearSessions(): Promise<void>;
static getSessionsByState(state): Promise<Session[]>;
static getLogRedirectionStrategy(): LogRedirectionStrategy;
static setLogRedirectionStrategy(logRedirectionStrategy: LogRedirectionStrategy);
static messagesInTransmit(sessionId: number): Promise<number>;
static sessionStateToString(state): string;
static parseArguments(command: string): string[];
static argumentsToString(commandArguments: string[]): string;
static enableLogs(): Promise<void>;
static disableLogs(): Promise<void>;
static enableStatistics(): Promise<void>;
static disableStatistics(): Promise<void>;
static getPlatform(): Promise<string>;
static writeToPipe(inputPath: string, pipePath: string): Promise<number>;
static selectDocumentForRead(type?: string, extraTypes?: string[]): Promise<string>;
static selectDocumentForWrite(title?: string, type?: string, extraTypes?: string[]): Promise<string>;
static getSafParameterForRead(uriString): Promise<string>;
static getSafParameterForWrite(uriString): Promise<string>;
}
export class FFmpegSession extends AbstractSession implements Session {
constructor();
static create(argumentsArray: Array<string>, executeCallback?: ExecuteCallback, logCallback?: LogCallback, statisticsCallback?: StatisticsCallback, logRedirectionStrategy?: LogRedirectionStrategy): Promise<FFmpegSession>;
static fromMap(sessionMap: { [key: string]: any }): FFmpegSession;
getStatisticsCallback(): StatisticsCallback;
getAllStatistics(waitTimeout?: number): Promise<Array<Statistics>>;
getStatistics(): Promise<Array<Statistics>>;
getLastReceivedStatistics(): Promise<Statistics>;
isFFmpeg(): boolean;
isFFprobe(): boolean;
}
export class FFprobeKit {
static executeAsync(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback): Promise<FFprobeSession>;
static executeWithArgumentsAsync(commandArguments: string[], executeCallback?: ExecuteCallback, logCallback?: LogCallback): Promise<FFprobeSession>;
static getMediaInformationAsync(path: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, waitTimeout?: number): Promise<MediaInformationSession>;
static getMediaInformationFromCommandAsync(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, waitTimeout?: number): Promise<MediaInformationSession>;
static getMediaInformationFromCommandArgumentsAsync(commandArguments: string[], executeCallback?: ExecuteCallback, logCallback?: LogCallback, waitTimeout?: number): Promise<MediaInformationSession>;
static listSessions(): Promise<FFprobeSession[]>;
}
export class FFprobeSession extends AbstractSession implements Session {
constructor();
static create(argumentsArray: Array<string>, executeCallback?: ExecuteCallback, logCallback?: LogCallback, logRedirectionStrategy?: LogRedirectionStrategy): Promise<FFprobeSession>;
static fromMap(sessionMap: { [key: string]: any }): FFprobeSession;
isFFmpeg(): boolean;
isFFprobe(): boolean;
}
export class Level {
static readonly AV_LOG_STDERR: number;
static readonly AV_LOG_QUIET: number;
static readonly AV_LOG_PANIC: number;
static readonly AV_LOG_FATAL: number;
static readonly AV_LOG_ERROR: number;
static readonly AV_LOG_WARNING: number;
static readonly AV_LOG_INFO: number;
static readonly AV_LOG_VERBOSE: number;
static readonly AV_LOG_DEBUG: number;
static readonly AV_LOG_TRACE: number;
static levelToString(number: number): string;
}
export class Log {
constructor(sessionId: number, level: number, message: String);
getSessionId(): number;
getLevel(): number;
getMessage(): String;
}
export type LogCallback = (log: Log) => void;
export enum LogRedirectionStrategy {
ALWAYS_PRINT_LOGS = 0,
PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED = 1,
PRINT_LOGS_WHEN_GLOBAL_CALLBACK_NOT_DEFINED = 2,
PRINT_LOGS_WHEN_SESSION_CALLBACK_NOT_DEFINED = 3,
NEVER_PRINT_LOGS = 4
}
export class MediaInformation {
static readonly KEY_MEDIA_PROPERTIES: string;
static readonly KEY_FILENAME: string;
static readonly KEY_FORMAT: string;
static readonly KEY_FORMAT_LONG: string;
static readonly KEY_START_TIME: string;
static readonly KEY_DURATION: string;
static readonly KEY_SIZE: string;
static readonly KEY_BIT_RATE: string;
static readonly KEY_TAGS: string;
constructor(properties: Record<string, any>);
getFilename(): string;
getFormat(): string;
getLongFormat(): string;
getDuration(): number;
getStartTime(): string;
getSize(): string;
getBitrate(): string;
getTags(): Record<string, any>;
getStreams(): Array<StreamInformation>;
getStringProperty(key: string): string;
getNumberProperty(key: string): number;
getProperties(key: string): Record<string, any>;
getMediaProperties(): Record<string, any>;
getAllProperties(): Record<string, any>;
}
export class MediaInformationJsonParser {
static from(ffprobeJsonOutput: string): Promise<MediaInformation>;
static fromWithError(ffprobeJsonOutput: string): Promise<MediaInformation>;
}
export class MediaInformationSession extends FFprobeSession {
constructor();
static create(argumentsArray: Array<string>, executeCallback?: ExecuteCallback, logCallback?: LogCallback): Promise<MediaInformationSession>;
static fromMap(sessionMap: { [key: string]: any }): MediaInformationSession;
getMediaInformation(): MediaInformation;
setMediaInformation(mediaInformation: MediaInformation): void;
}
export class Packages {
static getPackageName(): Promise<string>;
static getExternalLibraries(): Promise<string[]>;
}
export class ReturnCode {
static readonly SUCCESS: number;
static readonly CANCEL: number;
constructor(value: number);
static isSuccess(returnCode: ReturnCode): boolean;
static isCancel(returnCode: ReturnCode): boolean;
getValue(): number;
isValueSuccess(): boolean;
isValueError(): boolean;
isValueCancel(): boolean;
}
export interface Session {
getExecuteCallback(): ExecuteCallback;
getLogCallback(): LogCallback;
getSessionId(): number;
getCreateTime(): Date;
getStartTime(): Date;
getEndTime(): Promise<Date>;
getDuration(): Promise<number>;
getArguments(): Array<String>;
getCommand(): String;
getAllLogs(waitTimeout ?: number): Promise<Array<Log>>;
getLogs(): Promise<Array<Log>>;
getAllLogsAsString(waitTimeout?: number): Promise<string>;
getLogsAsString(): Promise<string>;
getOutput(): Promise<string>;
getState(): Promise<SessionState>;
getReturnCode(): Promise<ReturnCode>;
getFailStackTrace(): Promise<string>;
getLogRedirectionStrategy(): LogRedirectionStrategy;
thereAreAsynchronousMessagesInTransmit(): Promise<boolean>;
isFFmpeg(): boolean;
isFFprobe(): boolean;
cancel(): Promise<void>;
}
export enum SessionState {
CREATED = 0,
RUNNING = 1,
FAILED = 2,
COMPLETED = 3
}
export enum Signal {
SIGINT = 2,
SIGQUIT = 3,
SIGPIPE = 13,
SIGTERM = 15,
SIGXCPU = 24
}
export class Statistics {
constructor(sessionId: number, videoFrameNumber: number, videoFps: number, videoQuality: number, size: number, time: number, bitrate: number, speed: number);
getSessionId(): number;
setSessionId(sessionId: number): void;
getVideoFrameNumber(): number;
setVideoFrameNumber(videoFrameNumber: number): void;
getVideoFps(): number;
setVideoFps(videoFps: number): void;
getVideoQuality(): number;
setVideoQuality(videoQuality: number): void;
getSize(): number;
setSize(size: number): void;
getTime(): number;
setTime(time: number): void;
getBitrate(): number;
setBitrate(bitrate: number): void;
getSpeed(): number;
setSpeed(speed: number): void;
}
export type StatisticsCallback = (statistics: Statistics) => void;
export class StreamInformation {
static readonly KEY_INDEX: string;
static readonly KEY_TYPE: string;
static readonly KEY_CODEC: string;
static readonly KEY_CODEC_LONG: string;
static readonly KEY_FORMAT: string;
static readonly KEY_WIDTH: string;
static readonly KEY_HEIGHT: string;
static readonly KEY_BIT_RATE: string;
static readonly KEY_SAMPLE_RATE: string;
static readonly KEY_SAMPLE_FORMAT: string;
static readonly KEY_CHANNEL_LAYOUT: string;
static readonly KEY_SAMPLE_ASPECT_RATIO: string;
static readonly KEY_DISPLAY_ASPECT_RATIO: string;
static readonly KEY_AVERAGE_FRAME_RATE: string;
static readonly KEY_REAL_FRAME_RATE: string;
static readonly KEY_TIME_BASE: string;
static readonly KEY_CODEC_TIME_BASE: string;
static readonly KEY_TAGS: string;
constructor(properties: Record<string, any>);
getIndex(): number;
getType(): string;
getCodec(): string;
getCodecLong(): string;
getFormat(): string;
getWidth(): number;
getHeight(): number;
getBitrate(): string;
getSampleRate(): string;
getSampleFormat(): string;
getChannelLayout(): string;
getSampleAspectRatio(): string;
getDisplayAspectRatio(): string;
getAverageFrameRate(): string;
getRealFrameRate(): string;
getTimeBase(): string;
getCodecTimeBase(): string;
getTags(): Record<string, any>;
getStringProperty(key): string;
getNumberProperty(key): number;
getProperties(key): Record<string, any>;
getAllProperties(): Record<string, any>;
}
}

2750
react-native/src/index.js vendored Normal file

File diff suppressed because it is too large Load Diff