Compare commits

...

59 Commits

Author SHA1 Message Date
Taner Sener ae769f8cfd connect ffplay to sdl for android 2020-07-17 19:06:32 +01:00
Taner Sener 1da0c9d8d7 implement ffplay for android 2020-02-09 22:38:08 +00:00
Taner Sener 1883d97999 fix ios/tvos sdl flags 2020-01-30 22:03:15 +00:00
Taner Sener 46b538107b update release dates 2020-01-26 12:19:05 +00:00
Taner Sener fd11dd5004 apply workaround for aarch64 architectures, fixes #328 2020-01-25 10:06:22 +00:00
Taner Sener 9da84b3edb fix concurrent writer registration, fixes #324 2020-01-22 23:18:19 +00:00
Taner Sener 639dbb3ccb update clean.sh script 2020-01-19 11:16:43 +00:00
Hannes Achleitner d8b1754272 cleanup Android project 2020-01-16 20:49:23 +00:00
Taner Sener 73e2cc557f update wiki images 2020-01-16 19:41:51 +00:00
Taner Sener b70229b9e7 fix cancel operation 2020-01-14 19:53:54 +00:00
Taner Sener 47a9b728d7 update project main page 2020-01-14 00:16:17 +00:00
Taner Sener 32f25905cb update test-application versions 2020-01-13 23:37:03 +00:00
Taner Sener 35c26b390c freeze v4.3.1 2020-01-13 00:35:05 +00:00
Taner Sener b9549a04e7 fix duplicate symbol errors for fftools 2020-01-12 20:54:57 +00:00
Taner Sener c6840985e6 update copyright headers for android 2020-01-12 20:54:57 +00:00
Taner Sener 8e671be23d fix ffprobe optional parameter parsing errors. 2020-01-12 20:08:05 +00:00
Taner Sener 807051ce3c create scripts to build universal packages. 2020-01-12 20:06:22 +00:00
Taner Sener c38ebd52b6 update ios/tvos test applications 2020-01-12 20:05:13 +00:00
Taner Sener 37ba9a95c1 fix ios/tvos depreciation warnings 2020-01-12 10:11:13 +00:00
Taner Sener 99ae2531a4 fix ios tvos compilation for libvpx 2020-01-12 09:10:55 +00:00
Taner Sener 60accc6233 fix ffprobe android lts linking errors 2020-01-10 01:07:06 +00:00
Taner Sener 35fd709066 use new x264 version v20191125-stable 2020-01-09 23:01:48 +00:00
Taner Sener 57ebc9a177 fix libvpx ios compilation errors 2020-01-09 22:07:22 +00:00
Taner Sener dcb1935b57 fix wavpack ios compilation errors 2020-01-09 18:14:44 +00:00
Taner Sener 815b122cef use new ffmpeg version v4.3-dev-1944-g94cdf82d53 2020-01-09 01:18:19 +00:00
Taner Sener f3a279348d use new wavpack version v5.2.0 2020-01-09 01:06:30 +00:00
Taner Sener 920aa583b9 use new xvidcore version v1.3.7 2020-01-09 01:04:41 +00:00
Taner Sener 1e7a4bb3f2 use new libwebp version v1.1.0 2020-01-09 01:04:07 +00:00
Taner Sener e92ba21784 use new libjpeg-turbo version v2.0.4 2020-01-09 01:00:18 +00:00
Taner Sener 0d47a43829 use new libvpx version v1.8.2 2020-01-09 00:52:30 +00:00
Taner Sener 77d4f80ba6 use new libaom version v1.0.0-errata1-avif-110-gceb377078 2020-01-08 23:50:00 +00:00
Taner Sener ab0ecc624d use system libiconv on ios/tvos, fixes #230 2020-01-08 23:35:02 +00:00
Taner Sener 7d9747789b implement ffprobe, fixes #286 2020-01-08 23:27:35 +00:00
Taner Sener dfdadf656b use system libiconv on ios/tvos 2020-01-08 16:15:39 +00:00
Taner Sener ef03a31d23 update helper methods to reflect concurrent execution support changes, fixes #182 2020-01-03 12:41:19 +00:00
Taner Sener 8041678c13 Merge branch 'concurrent' into development 2020-01-03 00:38:13 +00:00
Taner Sener 283ebe29a4 fix report option and issue #302 2020-01-03 00:11:51 +00:00
Taner Sener bdbabb5a18 prevent libogg auto detection, fixes #307 2019-12-28 11:18:13 +03:00
Taner Sener 4e078bb9e8 support concurrent execution 2019-12-19 23:47:38 +00:00
Taner Sener 240def7581 fix x265 build errors 2019-12-19 19:38:16 +00:00
Taner Sener cc1a5bd4b2 update gradle version 2019-12-15 11:50:52 +00:00
Taner Sener 6ae6627d97 update library version 2019-12-15 11:30:52 +00:00
Taner Sener b151e6ab81 synchronise mobile-ffmpeg source code 2019-12-15 01:21:27 +00:00
Taner Sener e3c0e4d2e7 use new ffmpeg version v4.3-dev-1687 2019-12-14 23:38:18 +00:00
Taner Sener 9dec4e0353 update ignored files for new versions 2019-12-14 20:39:02 +00:00
Taner Sener 062ca46069 use new xvidcore version 1.3.6 2019-12-14 19:08:02 +00:00
Taner Sener 267e8236f5 use new gnutls version 3.6.11.1 2019-12-14 19:04:52 +00:00
Taner Sener 4f3380c7fe use new tiff version 4.1 2019-12-14 19:02:49 +00:00
Taner Sener 7692c42c64 use new fribidi version 1.0.8 2019-12-14 18:58:53 +00:00
Taner Sener 71a1289c32 fix tvos pod name 2019-11-25 20:27:49 +00:00
Javernaut 911a8f5478 [Android Demo] Cleaning Fragment classes 2019-11-21 18:35:31 +00:00
Javernaut 54fe0bd773 [Android Demo] Simplifying the layout for the main screen 2019-11-21 18:35:31 +00:00
Javernaut 7c99fcdeac [Android Demo] Removing custom header for the screen and using a standard ActionBar provided by the app theme 2019-11-21 18:35:31 +00:00
Javernaut 47c590c2b1 [Android Demo] Migrating the demo app to AndroidX 2019-11-21 18:35:31 +00:00
Javernaut 3e307bb4d6 [Android Demo] Removing worthless instrumentation tests from the demo app 2019-11-21 18:35:31 +00:00
Alexander Berezhnoi feece0ea50 Android library module improvement (#269)
* [Android Demo] Updating the Android Gradle Plugin to 3.5.2

* [Android Demo] Updating Gradle distribution to 6.0

* [Android Demo] Removing redundant dependencies in the library module

* [Android Demo] Removing worthless instrumentation test file from library module

* [Android Demo] Updating testing dependency in library module

* [Android Demo] Removing redundant resources from the library module

* Improving the android.sh, so it builds only necessary parts of the only library module

* Improving the android.sh. Cleaning only android library module when calling gradlew.

* [Android Demo] Updating Gradle dist to 6.0.1
2019-11-19 12:20:19 +00:00
Taner Sener 540e180e53 merge master README 2019-11-16 16:36:00 +00:00
Taner Sener 38398cb128 use new libxml2 version 2.9.10 2019-11-02 12:59:38 +00:00
Taner Sener a416890244 use new x265 version 3.2.1 2019-11-02 12:55:05 +00:00
4470 changed files with 188366 additions and 91172 deletions
+127 -46
View File
@@ -1,20 +1,15 @@
# MobileFFmpeg ![GitHub release](https://img.shields.io/badge/release-v4.3-blue.svg) ![Bintray](https://img.shields.io/badge/bintray-v4.3-blue.svg) ![CocoaPods](https://img.shields.io/badge/pod-v4.3-blue.svg) [![Build Status](https://travis-ci.org/tanersener/mobile-ffmpeg.svg?branch=master)](https://travis-ci.org/tanersener/mobile-ffmpeg)
# MobileFFmpeg [![Financial Contributors on Open Collective](https://opencollective.com/mobile-ffmpeg/all/badge.svg?label=financial+contributors)](https://opencollective.com/mobile-ffmpeg) ![GitHub release](https://img.shields.io/badge/release-v4.3.1-blue.svg) ![Bintray](https://img.shields.io/badge/bintray-v4.3.1-blue.svg) ![CocoaPods](https://img.shields.io/badge/pod-v4.3.1-blue.svg) [![Build Status](https://travis-ci.org/tanersener/mobile-ffmpeg.svg?branch=master)](https://travis-ci.org/tanersener/mobile-ffmpeg)
FFmpeg for Android, iOS and tvOS
<img src="https://github.com/tanersener/mobile-ffmpeg/blob/master/docs/assets/mobile-ffmpeg-logo-v7.png" width="320">
### 0. Backers via Patreon
<a href="https://github.com/wjzhangcsu/" target="_blank">
<img width="40px" src="https://avatars0.githubusercontent.com/u/11661685?s=460&v=4">
</a>
### 1. Features
- Includes both `FFmpeg` and `FFprobe`
- Use binaries available at `Github`/`JCenter`/`CocoaPods` or build your own version with external libraries you need
- Supports
- Android, iOS and tvOS
- FFmpeg `v3.4.x`, `v4.0.x`, `v4.1`, `v4.2-dev` and `v4.3-dev` releases
- FFmpeg `v3.4.x`, `v4.0.x`, `v4.1`, `v4.2` and `v4.3-dev` releases
- 28 external libraries
`chromaprint`, `fontconfig`, `freetype`, `fribidi`, `gmp`, `gnutls`, `kvazaar`, `lame`, `libaom`, `libass`, `libiconv`, `libilbc`, `libtheora`, `libvorbis`, `libvpx`, `libwebp`, `libxml2`, `opencore-amr`, `openh264`, `opus`, `sdl`, `shine`, `snappy`, `soxr`, `speex`, `tesseract`, `twolame`, `wavpack`
@@ -23,6 +18,8 @@ FFmpeg for Android, iOS and tvOS
`vid.stab`, `x264`, `x265`, `xvidcore`
- Concurrent execution
- Exposes both FFmpeg library and MobileFFmpeg wrapper library capabilities
- Includes cross-compile instructions for 44 open-source libraries
@@ -40,7 +37,7 @@ FFmpeg for Android, iOS and tvOS
#### 1.2 iOS
- Builds `armv7`, `armv7s`, `arm64`, `arm64e`, `i386` and `x86_64` architectures
- Supports `bzip2`, `zlib` system libraries and `AudioToolbox`, `CoreImage`, `VideoToolbox`, `AVFoundation` system frameworks
- Supports `bzip2`, `zlib`, `iconv` system libraries and `AudioToolbox`, `CoreImage`, `VideoToolbox`, `AVFoundation` system frameworks
- Objective-C API
- Camera access
- `ARC` enabled library
@@ -50,7 +47,7 @@ FFmpeg for Android, iOS and tvOS
#### 1.3 tvOS
- Builds `arm64` and `x86_64` architectures
- Supports `bzip2`, `zlib` system libraries and `AudioToolbox`, `CoreImage`, `VideoToolbox` system frameworks
- Supports `bzip2`, `zlib`, `iconv` system libraries and `AudioToolbox`, `CoreImage`, `VideoToolbox` system frameworks
- Objective-C API
- `ARC` enabled library
- Built with `-fembed-bitcode` flag
@@ -96,11 +93,11 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
</tr>
<tr>
<td align="center"><sup>ios system libraries</sup></td>
<td align="center" colspan=8><sup>zlib</sup><br><sup>AudioToolbox</sup><br><sup>AVFoundation</sup><br><sup>CoreImage</sup><br><sup>VideoToolbox</sup><br><sup>bzip2</sup></td>
<td align="center" colspan=8><sup>zlib</sup><br><sup>AudioToolbox</sup><br><sup>AVFoundation</sup><br><sup>CoreImage</sup><br><sup>iconv</sup><br><sup>VideoToolbox</sup><br><sup>bzip2</sup></td>
</tr>
<tr>
<td align="center"><sup>tvos system libraries</sup></td>
<td align="center" colspan=8><sup>zlib</sup><br><sup>AudioToolbox</sup><br><sup>CoreImage</sup><br><sup>VideoToolbox</sup><br><sup>bzip2</sup></td>
<td align="center" colspan=8><sup>zlib</sup><br><sup>AudioToolbox</sup><br><sup>CoreImage</sup><br><sup>iconv</sup><br><sup>VideoToolbox</sup><br><sup>bzip2</sup></td>
</tr>
</tbody>
</table>
@@ -116,25 +113,52 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
- Since `v4.2`, `chromaprint`, `sdl` and `tesseract` libraries are not included in binary releases. You can still build them and include in your releases
- `AVFoundation` is not available on `tvOS`, `VideoToolbox` is not available on `tvOS` LTS releases
- Since `v4.3.1`, `iOS` and `tvOS` releases started to use `iconv` system library instead of `iconv` external library
#### 2.1 Android
1. Add MobileFFmpeg dependency to your `build.gradle` in `mobile-ffmpeg-<package name>` format
```
dependencies {
implementation 'com.arthenica:mobile-ffmpeg-full:4.3'
implementation 'com.arthenica:mobile-ffmpeg-full:4.3.1'
}
```
2. Execute commands.
2. Execute FFmpeg commands.
```
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFmpeg;
FFmpeg.execute("-i file1.mp4 -c:v mpeg4 file2.mp4");
int rc = FFmpeg.execute("-i file1.mp4 -c:v mpeg4 file2.mp4");
if (rc == RETURN_CODE_SUCCESS) {
Log.i(Config.TAG, "Command execution completed successfully.");
} else if (rc == RETURN_CODE_CANCEL) {
Log.i(Config.TAG, "Command execution cancelled by user.");
} else {
Log.i(Config.TAG, String.format("Command execution failed with rc=%d and the output below.", rc));
Config.printLastCommandOutput(Log.INFO);
}
```
3. Check execution output.
3. Execute FFprobe commands.
```
int rc = FFmpeg.getLastReturnCode();
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFprobe;
int rc = FFprobe.execute("-i file1.mp4");
if (rc == RETURN_CODE_SUCCESS) {
Log.i(Config.TAG, "Command execution completed successfully.");
} else {
Log.i(Config.TAG, String.format("Command execution failed with rc=%d and the output below.", rc));
Config.printLastCommandOutput(Log.INFO);
}
```
4. Check execution output later.
```
int rc = Config.getLastReturnCode();
if (rc == RETURN_CODE_SUCCESS) {
Log.i(Config.TAG, "Command execution completed successfully.");
@@ -142,31 +166,31 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
Log.i(Config.TAG, "Command execution cancelled by user.");
} else {
Log.i(Config.TAG, String.format("Command execution failed with rc=%d and the output below.", rc));
FFmpeg.printLastCommandOutput(Log.INFO);
Config.printLastCommandOutput(Log.INFO);
}
```
4. Stop an ongoing operation.
5. Stop an ongoing FFmpeg operation.
```
FFmpeg.cancel();
```
5. Get media information for a file.
6. Get media information for a file.
```
MediaInformation info = FFmpeg.getMediaInformation("<file path or uri>");
MediaInformation info = FFprobe.getMediaInformation("<file path or uri>");
```
6. Record video using Android camera.
7. Record video using Android camera.
```
FFmpeg.execute("-f android_camera -i 0:0 -r 30 -pixel_format bgr0 -t 00:00:05 <record file path>");
```
7. List enabled external libraries.
8. List enabled external libraries.
```
List<String> externalLibraries = Config.getExternalLibraries();
```
8. Enable log callback.
9. Enable log callback.
```
Config.enableLogCallback(new LogCallback() {
public void apply(LogMessage message) {
@@ -175,7 +199,7 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
});
```
9. Enable statistics callback.
10. Enable statistics callback.
```
Config.enableStatisticsCallback(new StatisticsCallback() {
public void apply(Statistics newStatistics) {
@@ -184,12 +208,12 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
});
```
10. Set log level.
11. Set log level.
```
Config.setLogLevel(Level.AV_LOG_FATAL);
```
11. Register custom fonts directory.
12. Register custom fonts directory.
```
Config.setFontDirectory(this, "<folder with fonts>", Collections.EMPTY_MAP);
```
@@ -199,25 +223,50 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
- iOS
```
pod 'mobile-ffmpeg-full', '~> 4.3'
pod 'mobile-ffmpeg-full', '~> 4.3.1'
```
- tvOS
```
pod 'mobile-ffmpeg-tv-full', '~> 4.3'
pod 'mobile-ffmpeg-tvos-full', '~> 4.3.1'
```
2. Execute commands.
2. Execute FFmpeg commands.
```
#import <mobileffmpeg/MobileFFmpegConfig.h>
#import <mobileffmpeg/MobileFFmpeg.h>
[MobileFFmpeg execute: @"-i file1.mp4 -c:v mpeg4 file2.mp4"];
int rc = [MobileFFmpeg execute: @"-i file1.mp4 -c:v mpeg4 file2.mp4"];
if (rc == RETURN_CODE_SUCCESS) {
NSLog(@"Command execution completed successfully.\n");
} else if (rc == RETURN_CODE_CANCEL) {
NSLog(@"Command execution cancelled by user.\n");
} else {
NSLog(@"Command execution failed with rc=%d and output=%@.\n", rc, [MobileFFmpegConfig getLastCommandOutput]);
}
```
3. Check execution output.
3. Execute FFprobe commands.
```
int rc = [MobileFFmpeg getLastReturnCode];
NSString *output = [MobileFFmpeg getLastCommandOutput];
#import <mobileffmpeg/MobileFFmpegConfig.h>
#import <mobileffmpeg/MobileFFprobe.h>
int rc = [MobileFFprobe execute: @"-i file1.mp4"];
if (rc == RETURN_CODE_SUCCESS) {
NSLog(@"Command execution completed successfully.\n");
} else if (rc == RETURN_CODE_CANCEL) {
NSLog(@"Command execution cancelled by user.\n");
} else {
NSLog(@"Command execution failed with rc=%d and output=%@.\n", rc, [MobileFFmpegConfig getLastCommandOutput]);
}
```
4. Check execution output later.
```
int rc = [MobileFFmpegConfig getLastReturnCode];
NSString *output = [MobileFFmpegConfig getLastCommandOutput];
if (rc == RETURN_CODE_SUCCESS) {
NSLog(@"Command execution completed successfully.\n");
@@ -228,28 +277,28 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
}
```
4. Stop an ongoing operation.
5. Stop an ongoing FFmpeg operation.
```
[MobileFFmpeg cancel];
```
5. Get media information for a file.
6. Get media information for a file.
```
MediaInformation *mediaInformation = [MobileFFmpeg getMediaInformation:@"<file path or uri>"];
MediaInformation *mediaInformation = [MobileFFprobe getMediaInformation:@"<file path or uri>"];
```
6. Record video and audio using iOS camera. This operation is not supported on `tvOS` since `AVFoundation` is not available on `tvOS`.
7. Record video and audio using iOS camera. This operation is not supported on `tvOS` since `AVFoundation` is not available on `tvOS`.
```
[MobileFFmpeg execute: @"-f avfoundation -r 30 -video_size 1280x720 -pixel_format bgr0 -i 0:0 -vcodec h264_videotoolbox -vsync 2 -f h264 -t 00:00:05 %@", recordFilePath];
```
7. List enabled external libraries.
8. List enabled external libraries.
```
NSArray *externalLibraries = [MobileFFmpegConfig getExternalLibraries];
```
8. Enable log callback.
9. Enable log callback.
```
[MobileFFmpegConfig setLogDelegate:self];
@@ -260,7 +309,7 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
}
```
9. Enable statistics callback.
10. Enable statistics callback.
```
[MobileFFmpegConfig setStatisticsDelegate:self];
@@ -271,12 +320,12 @@ Please remember that some parts of `FFmpeg` are licensed under the `GPL` and onl
}
```
10. Set log level.
11. Set log level.
```
[MobileFFmpegConfig setLogLevel:AV_LOG_FATAL];
```
11. Register custom fonts directory.
12. Register custom fonts directory.
```
[MobileFFmpegConfig setFontDirectory:@"<folder with fonts>" with:nil];
```
@@ -313,6 +362,8 @@ Exact version number is obtained using `git describe --tags`.
| MobileFFmpeg Version | FFmpeg Version | Release Date |
| :----: | :----: |:----: |
| [4.3.1](https://github.com/tanersener/mobile-ffmpeg/releases/tag/v4.3.1) | 4.3-dev-1944 | Jan 25, 2020 |
| [4.3.1.LTS](https://github.com/tanersener/mobile-ffmpeg/releases/tag/v4.3.1.LTS) | 4.3-dev-1944 | Jan 25, 2020 |
| [4.3](https://github.com/tanersener/mobile-ffmpeg/releases/tag/v4.3) | 4.3-dev-1181 | Oct 27, 2019 |
| [4.2.2](https://github.com/tanersener/mobile-ffmpeg/releases/tag/v4.2.2) | 4.2-dev-1824 | July 3, 2019 |
| [4.2.2.LTS](https://github.com/tanersener/mobile-ffmpeg/releases/tag/v4.2.2.LTS) | 4.2-dev-1824 | July 3, 2019 |
@@ -448,7 +499,37 @@ CPU optimizations (`ASM`) are enabled for most of the external libraries. Detail
A more detailed documentation is available at [Wiki](https://github.com/tanersener/mobile-ffmpeg/wiki).
### 7. License
### 7. Contributors
#### 7.1 Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/tanersener/mobile-ffmpeg/graphs/contributors"><img src="https://opencollective.com/mobile-ffmpeg/contributors.svg?width=890&button=false" /></a>
#### 7.2 Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/mobile-ffmpeg/contribute)]
##### 7.2.1 Individuals
<a href="https://opencollective.com/mobile-ffmpeg"><img src="https://opencollective.com/mobile-ffmpeg/individuals.svg?width=890"></a>
##### 7.2.2 Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/mobile-ffmpeg/contribute)]
<a href="https://opencollective.com/mobile-ffmpeg/organization/0/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/1/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/2/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/3/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/4/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/5/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/6/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/7/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/8/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/mobile-ffmpeg/organization/9/website"><img src="https://opencollective.com/mobile-ffmpeg/organization/9/avatar.svg"></a>
### 8. License
This project is licensed under the LGPL v3.0. However, if source code is built using optional `--enable-gpl` flag or
prebuilt binaries with `-gpl` postfix are used then MobileFFmpeg is subject to the GPL v3.0 license.
@@ -466,11 +547,11 @@ In test applications; embedded fonts are licensed under the [SIL Open Font Licen
Please visit [License](https://github.com/tanersener/mobile-ffmpeg/wiki/License) page for the details.
### 8. Contributing
### 9. Contributing
If you have any recommendations or ideas to improve it, please feel free to submit issues or pull requests. Any help is appreciated.
### 9. See Also
### 10. See Also
- [libav gas-preprocessor](https://github.com/libav/gas-preprocessor/raw/master/gas-preprocessor.pl)
- [FFmpeg API Documentation](https://ffmpeg.org/doxygen/4.0/index.html)
+12 -3
View File
@@ -82,7 +82,7 @@ without any external libraries enabled. Options can be used to disable ABIs and/
Please note that GPL libraries (external libraries with GPL license) need --enable-gpl flag to be set explicitly. \
When compilation ends an Android Archive (AAR) file is created with enabled platforms inside.\n"
echo -e "Usage: ./"$COMMAND" [OPTION]...\n"
echo -e "Usage: ./"$COMMAND" [OPTION]...\n"
echo -e "Specify environment variables as VARIABLE=VALUE to override default build options.\n"
@@ -780,6 +780,7 @@ done
export API=${ORIGINAL_API}
# DEFINE ANDROID ARCHITECTURES
rm -f ${BASEDIR}/android/build/.neon 1>>${BASEDIR}/build.log 2>&1
ANDROID_ARCHITECTURES=""
if [[ ${ENABLED_ARCHITECTURES[1]} -eq 1 ]]; then
@@ -800,6 +801,14 @@ if [[ ${ENABLED_ARCHITECTURES[4]} -eq 1 ]]; then
ANDROID_ARCHITECTURES+="$(get_android_arch 4) "
fi
# DEFINE BUILD FFPLAY FLAG
rm -f ${BASEDIR}/android/build/.ffplay 1>>${BASEDIR}/build.log 2>&1
if [[ ${ENABLED_LIBRARIES[LIBRARY_SDL]} -eq 1 ]]; then
mkdir -p ${BASEDIR}/android/build 1>>${BASEDIR}/build.log 2>&1
cat > "${BASEDIR}/android/build/.ffplay" << EOF
EOF
fi
if [[ ! -z ${ANDROID_ARCHITECTURES} ]]; then
echo -n -e "\nmobile-ffmpeg: "
@@ -826,14 +835,14 @@ if [[ ! -z ${ANDROID_ARCHITECTURES} ]]; then
echo -e -n "\n\nCreating Android archive under prebuilt/android-aar: "
./gradlew clean build 1>>${BASEDIR}/build.log 2>&1
./gradlew app:clean app:assembleRelease app:testReleaseUnitTest 1>>${BASEDIR}/build.log 2>&1
if [ $? -ne 0 ]; then
echo -e "failed\n"
exit 1
fi
cp ${BASEDIR}/android/app/build/outputs/aar/mobile-ffmpeg.aar ${MOBILE_FFMPEG_AAR}/mobile-ffmpeg.aar 1>>${BASEDIR}/build.log 2>&1
cp ${BASEDIR}/android/app/build/outputs/aar/mobile-ffmpeg-release.aar ${MOBILE_FFMPEG_AAR}/mobile-ffmpeg.aar 1>>${BASEDIR}/build.log 2>&1
if [ $? -ne 0 ]; then
echo -e "failed\n"
+6 -8
View File
@@ -1,12 +1,10 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
local.properties
.DS_Store
/build
/captures
build
captures
.externalNativeBuild
/.idea/
/obj/
/libs/
.idea
obj
libs
-2
View File
@@ -1,2 +0,0 @@
/build
/local.properties
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = "MobileFFmpeg Android API"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 4.3
PROJECT_NUMBER = 4.3.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+5 -7
View File
@@ -1,13 +1,13 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
minSdkVersion 24
targetSdkVersion 28
versionCode 240430
versionName "4.3"
targetSdkVersion 29
versionCode 240431
versionName "4.3.1"
project.archivesBaseName = "mobile-ffmpeg"
consumerProguardFiles 'proguard-rules.pro'
}
@@ -38,7 +38,5 @@ task javadoc(type: Javadoc) {
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
testImplementation "androidx.test.ext:junit:1.1.1"
}
+194 -148
View File
@@ -20,6 +20,14 @@
*/
/*
* CHANGES 01.2020
* - ffprobe support changes
* - (optindex < argc) validation in parse_options() method updated with (optindex >= argc) check
*
* CHANGES 12.2019
* - Concurrent execution support
* - log_callback_report method re-added to fix -report option issues
*
* CHANGES 08.2018
* --------------------------------------------------------
* - fftools_ prefix added to file name and parent header
@@ -85,15 +93,19 @@
static int init_report(const char *env);
extern void mobileffmpeg_log_callback_function(void *ptr, int level, const char* format, va_list vargs);
extern void (*report_callback)(int, float, float, int64_t, int, double, double);
AVDictionary *sws_dict;
AVDictionary *swr_opts;
AVDictionary *format_opts, *codec_opts, *resample_opts;
__thread char *program_name;
__thread int program_birth_year;
static FILE *report_file;
static int report_file_level = AV_LOG_DEBUG;
int hide_banner = 0;
int longjmp_value = 0;
__thread AVDictionary *sws_dict;
__thread AVDictionary *swr_opts;
__thread AVDictionary *format_opts, *codec_opts, *resample_opts;
FILE *report_file;
int report_file_level = AV_LOG_DEBUG;
__thread int hide_banner = 0;
__thread int longjmp_value = 0;
enum show_muxdemuxers {
SHOW_DEFAULT,
@@ -115,9 +127,29 @@ void uninit_opts(void)
av_dict_free(&resample_opts);
}
void log_callback_report(void *ptr, int level, const char *fmt, va_list vl)
{
va_list vl2;
char line[1024];
static int print_prefix = 1;
va_copy(vl2, vl);
if (report_callback == NULL) {
av_log_default_callback(ptr, level, fmt, vl);
} else {
mobileffmpeg_log_callback_function(ptr, level, fmt, vl);
}
av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
va_end(vl2);
if (report_file_level >= level) {
fputs(line, report_file);
fflush(report_file);
}
}
void init_dynload(void)
{
#ifdef _WIN32
#if HAVE_SETDLLDIRECTORY
/* Calling SetDllDirectory with the empty string (but not NULL) removes the
* current working directory from the DLL search path as a security pre-caution. */
SetDllDirectory("");
@@ -191,7 +223,7 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags,
continue;
if (first) {
av_log(NULL, AV_LOG_INFO, "%s\n", msg);
av_log(NULL, AV_LOG_STDERR, "%s\n", msg);
first = 0;
}
av_strlcpy(buf, po->name, sizeof(buf));
@@ -199,9 +231,9 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags,
av_strlcat(buf, " ", sizeof(buf));
av_strlcat(buf, po->argname, sizeof(buf));
}
av_log(NULL, AV_LOG_INFO, "-%-17s %s\n", buf, po->help);
av_log(NULL, AV_LOG_STDERR, "-%-17s %s\n", buf, po->help);
}
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "\n");
}
void show_help_children(const AVClass *class, int flags)
@@ -209,7 +241,7 @@ void show_help_children(const AVClass *class, int flags)
const AVClass *child = NULL;
if (class->option) {
av_opt_show2(&class, NULL, flags, 0);
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "\n");
}
while ((child = av_opt_child_class_next(class, child)))
@@ -401,12 +433,14 @@ void parse_options(void *optctx, int argc, char **argv, const OptionDef *options
continue;
}
opt++;
if (optindex < argc) {
if (optindex >= argc) {
if ((ret = parse_option(optctx, opt, NULL, options)) < 0)
exit_program(1);
} else {
if ((ret = parse_option(optctx, opt, argv[optindex], options)) < 0)
exit_program(1);
optindex += ret;
}
optindex += ret;
} else {
if (parse_arg_function)
parse_arg_function(optctx, opt);
@@ -473,7 +507,7 @@ int locate_option(int argc, char **argv, const OptionDef *options,
return 0;
}
static void dump_argument(const char *a)
void dump_argument(const char *a)
{
const unsigned char *p;
@@ -812,7 +846,7 @@ do { \
if (optindex < argc) {
arg = argv[optindex++];
} else {
arg = "";
arg = NULL;
}
} else if (po->flags & HAS_ARG) {
GET_ARG(arg);
@@ -856,8 +890,8 @@ do { \
}
if (octx->cur_group.nb_opts || codec_opts || format_opts || resample_opts)
av_log(NULL, AV_LOG_WARNING, "Trailing options were found on the "
"commandline.\n");
av_log(NULL, AV_LOG_WARNING, "Trailing option(s) found in the "
"command: may be ignored.\n");
av_log(NULL, AV_LOG_DEBUG, "Finished splitting the commandline.\n");
@@ -983,11 +1017,12 @@ static void expand_filename_template(AVBPrint *bp, const char *template,
}
}
static int init_report(const char *env)
int init_report(const char *env)
{
char *filename_template = NULL;
char *key, *val;
int ret, count = 0;
int prog_loglevel, envlevel = 0;
time_t now;
struct tm *tm;
AVBPrint filename;
@@ -1019,6 +1054,7 @@ static int init_report(const char *env)
av_log(NULL, AV_LOG_FATAL, "Invalid report file level\n");
exit_program(1);
}
envlevel = 1;
} else {
av_log(NULL, AV_LOG_ERROR, "Unknown key '%s' in FFREPORT\n", key);
}
@@ -1035,6 +1071,10 @@ static int init_report(const char *env)
return AVERROR(ENOMEM);
}
prog_loglevel = av_log_get_level();
if (!envlevel)
report_file_level = FFMAX(report_file_level, prog_loglevel);
report_file = fopen(filename.str, "w");
if (!report_file) {
int ret = AVERROR(errno);
@@ -1042,19 +1082,20 @@ static int init_report(const char *env)
filename.str, strerror(errno));
return ret;
}
av_log_set_callback(mobileffmpeg_log_callback_function);
av_log_set_callback(log_callback_report);
av_log(NULL, AV_LOG_INFO,
"%s started on %04d-%02d-%02d at %02d:%02d:%02d\n"
"Report written to \"%s\"\n",
"Report written to \"%s\"\n"
"Log level: %d\n",
program_name,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
filename.str);
filename.str, report_file_level);
av_bprint_finalize(&filename, NULL);
return 0;
}
int opt_report(const char *opt)
int opt_report(void *optctx, const char *opt, const char *arg)
{
return init_report(NULL);
}
@@ -1096,7 +1137,7 @@ void print_error(const char *filename, int err)
av_log(NULL, AV_LOG_ERROR, "%s: %s\n", filename, errbuf_ptr);
}
static int warned_cfg = 0;
__thread int warned_cfg = 0;
#define INDENT 1
#define SHOW_VERSION 2
@@ -1196,7 +1237,6 @@ void show_banner(int argc, char **argv, const OptionDef *options)
int show_version(void *optctx, const char *opt, const char *arg)
{
av_log_set_callback(mobileffmpeg_log_callback_function);
print_program_info (SHOW_COPYRIGHT, AV_LOG_INFO);
print_all_libs_info(SHOW_VERSION, AV_LOG_INFO);
@@ -1205,7 +1245,6 @@ int show_version(void *optctx, const char *opt, const char *arg)
int show_buildconf(void *optctx, const char *opt, const char *arg)
{
av_log_set_callback(mobileffmpeg_log_callback_function);
print_buildconf (INDENT|0, AV_LOG_INFO);
return 0;
@@ -1214,12 +1253,12 @@ int show_buildconf(void *optctx, const char *opt, const char *arg)
int show_license(void *optctx, const char *opt, const char *arg)
{
#if CONFIG_NONFREE
av_log(NULL, AV_LOG_INFO,
av_log(NULL, AV_LOG_STDERR,
"This version of %s has nonfree parts compiled in.\n"
"Therefore it is not legally redistributable.\n",
program_name );
#elif CONFIG_GPLV3
av_log(NULL, AV_LOG_INFO,
av_log(NULL, AV_LOG_STDERR,
"%s is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 3 of the License, or\n"
@@ -1234,7 +1273,7 @@ int show_license(void *optctx, const char *opt, const char *arg)
"along with %s. If not, see <http://www.gnu.org/licenses/>.\n",
program_name, program_name, program_name );
#elif CONFIG_GPL
av_log(NULL, AV_LOG_INFO,
av_log(NULL, AV_LOG_STDERR,
"%s is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
@@ -1250,7 +1289,7 @@ int show_license(void *optctx, const char *opt, const char *arg)
"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n",
program_name, program_name, program_name );
#elif CONFIG_LGPLV3
av_log(NULL, AV_LOG_INFO,
av_log(NULL, AV_LOG_STDERR,
"%s is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU Lesser General Public License as published by\n"
"the Free Software Foundation; either version 3 of the License, or\n"
@@ -1265,7 +1304,7 @@ int show_license(void *optctx, const char *opt, const char *arg)
"along with %s. If not, see <http://www.gnu.org/licenses/>.\n",
program_name, program_name, program_name );
#else
av_log(NULL, AV_LOG_INFO,
av_log(NULL, AV_LOG_STDERR,
"%s is free software; you can redistribute it and/or\n"
"modify it under the terms of the GNU Lesser General Public\n"
"License as published by the Free Software Foundation; either\n"
@@ -1301,7 +1340,7 @@ static int show_formats_devices(void *optctx, const char *opt, const char *arg,
const char *last_name;
int is_dev;
av_log(NULL, AV_LOG_INFO, "%s\n"
av_log(NULL, AV_LOG_STDERR, "%s\n"
" D. = Demuxing supported\n"
" .E = Muxing supported\n"
" --\n", device_only ? "Devices:" : "File formats:");
@@ -1346,7 +1385,7 @@ static int show_formats_devices(void *optctx, const char *opt, const char *arg,
break;
last_name = name;
av_log(NULL, AV_LOG_INFO, " %s%s %-15s %s\n",
av_log(NULL, AV_LOG_STDERR, " %s%s %-15s %s\n",
decode ? "D" : " ",
encode ? "E" : " ",
name,
@@ -1379,97 +1418,97 @@ int show_devices(void *optctx, const char *opt, const char *arg)
if (codec->field) { \
const type *p = codec->field; \
\
av_log(NULL, AV_LOG_INFO, " Supported " list_name ":"); \
av_log(NULL, AV_LOG_STDERR, " Supported " list_name ":"); \
while (*p != term) { \
get_name(*p); \
av_log(NULL, AV_LOG_INFO, " %s", name); \
av_log(NULL, AV_LOG_STDERR, " %s", name); \
p++; \
} \
av_log(NULL, AV_LOG_INFO, "\n"); \
av_log(NULL, AV_LOG_STDERR, "\n"); \
} \
static void print_codec(const AVCodec *c)
{
int encoder = av_codec_is_encoder(c);
av_log(NULL, AV_LOG_INFO, "%s %s [%s]:\n", encoder ? "Encoder" : "Decoder", c->name,
av_log(NULL, AV_LOG_STDERR, "%s %s [%s]:\n", encoder ? "Encoder" : "Decoder", c->name,
c->long_name ? c->long_name : "");
av_log(NULL, AV_LOG_INFO, " General capabilities: ");
av_log(NULL, AV_LOG_STDERR, " General capabilities: ");
if (c->capabilities & AV_CODEC_CAP_DRAW_HORIZ_BAND)
av_log(NULL, AV_LOG_INFO, "horizband ");
av_log(NULL, AV_LOG_STDERR, "horizband ");
if (c->capabilities & AV_CODEC_CAP_DR1)
av_log(NULL, AV_LOG_INFO, "dr1 ");
av_log(NULL, AV_LOG_STDERR, "dr1 ");
if (c->capabilities & AV_CODEC_CAP_TRUNCATED)
av_log(NULL, AV_LOG_INFO, "trunc ");
av_log(NULL, AV_LOG_STDERR, "trunc ");
if (c->capabilities & AV_CODEC_CAP_DELAY)
av_log(NULL, AV_LOG_INFO, "delay ");
av_log(NULL, AV_LOG_STDERR, "delay ");
if (c->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME)
av_log(NULL, AV_LOG_INFO, "small ");
av_log(NULL, AV_LOG_STDERR, "small ");
if (c->capabilities & AV_CODEC_CAP_SUBFRAMES)
av_log(NULL, AV_LOG_INFO, "subframes ");
av_log(NULL, AV_LOG_STDERR, "subframes ");
if (c->capabilities & AV_CODEC_CAP_EXPERIMENTAL)
av_log(NULL, AV_LOG_INFO, "exp ");
av_log(NULL, AV_LOG_STDERR, "exp ");
if (c->capabilities & AV_CODEC_CAP_CHANNEL_CONF)
av_log(NULL, AV_LOG_INFO, "chconf ");
av_log(NULL, AV_LOG_STDERR, "chconf ");
if (c->capabilities & AV_CODEC_CAP_PARAM_CHANGE)
av_log(NULL, AV_LOG_INFO, "paramchange ");
av_log(NULL, AV_LOG_STDERR, "paramchange ");
if (c->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)
av_log(NULL, AV_LOG_INFO, "variable ");
av_log(NULL, AV_LOG_STDERR, "variable ");
if (c->capabilities & (AV_CODEC_CAP_FRAME_THREADS |
AV_CODEC_CAP_SLICE_THREADS |
AV_CODEC_CAP_AUTO_THREADS))
av_log(NULL, AV_LOG_INFO, "threads ");
av_log(NULL, AV_LOG_STDERR, "threads ");
if (c->capabilities & AV_CODEC_CAP_AVOID_PROBING)
av_log(NULL, AV_LOG_INFO, "avoidprobe ");
av_log(NULL, AV_LOG_STDERR, "avoidprobe ");
if (c->capabilities & AV_CODEC_CAP_INTRA_ONLY)
av_log(NULL, AV_LOG_INFO, "intraonly ");
av_log(NULL, AV_LOG_STDERR, "intraonly ");
if (c->capabilities & AV_CODEC_CAP_LOSSLESS)
av_log(NULL, AV_LOG_INFO, "lossless ");
av_log(NULL, AV_LOG_STDERR, "lossless ");
if (c->capabilities & AV_CODEC_CAP_HARDWARE)
av_log(NULL, AV_LOG_INFO, "hardware ");
av_log(NULL, AV_LOG_STDERR, "hardware ");
if (c->capabilities & AV_CODEC_CAP_HYBRID)
av_log(NULL, AV_LOG_INFO, "hybrid ");
av_log(NULL, AV_LOG_STDERR, "hybrid ");
if (!c->capabilities)
av_log(NULL, AV_LOG_INFO, "none");
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "none");
av_log(NULL, AV_LOG_STDERR, "\n");
if (c->type == AVMEDIA_TYPE_VIDEO ||
c->type == AVMEDIA_TYPE_AUDIO) {
av_log(NULL, AV_LOG_INFO, " Threading capabilities: ");
av_log(NULL, AV_LOG_STDERR, " Threading capabilities: ");
switch (c->capabilities & (AV_CODEC_CAP_FRAME_THREADS |
AV_CODEC_CAP_SLICE_THREADS |
AV_CODEC_CAP_AUTO_THREADS)) {
case AV_CODEC_CAP_FRAME_THREADS |
AV_CODEC_CAP_SLICE_THREADS: av_log(NULL, AV_LOG_INFO, "frame and slice"); break;
case AV_CODEC_CAP_FRAME_THREADS: av_log(NULL, AV_LOG_INFO, "frame"); break;
case AV_CODEC_CAP_SLICE_THREADS: av_log(NULL, AV_LOG_INFO, "slice"); break;
case AV_CODEC_CAP_AUTO_THREADS : av_log(NULL, AV_LOG_INFO, "auto"); break;
default: av_log(NULL, AV_LOG_INFO, "none"); break;
AV_CODEC_CAP_SLICE_THREADS: av_log(NULL, AV_LOG_STDERR, "frame and slice"); break;
case AV_CODEC_CAP_FRAME_THREADS: av_log(NULL, AV_LOG_STDERR, "frame"); break;
case AV_CODEC_CAP_SLICE_THREADS: av_log(NULL, AV_LOG_STDERR, "slice"); break;
case AV_CODEC_CAP_AUTO_THREADS : av_log(NULL, AV_LOG_STDERR, "auto"); break;
default: av_log(NULL, AV_LOG_STDERR, "none"); break;
}
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "\n");
}
if (avcodec_get_hw_config(c, 0)) {
av_log(NULL, AV_LOG_INFO, " Supported hardware devices: ");
av_log(NULL, AV_LOG_STDERR, " Supported hardware devices: ");
for (int i = 0;; i++) {
const AVCodecHWConfig *config = avcodec_get_hw_config(c, i);
if (!config)
break;
av_log(NULL, AV_LOG_INFO, "%s ", av_hwdevice_get_type_name(config->device_type));
av_log(NULL, AV_LOG_STDERR, "%s ", av_hwdevice_get_type_name(config->device_type));
}
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "\n");
}
if (c->supported_framerates) {
const AVRational *fps = c->supported_framerates;
av_log(NULL, AV_LOG_INFO, " Supported framerates:");
av_log(NULL, AV_LOG_STDERR, " Supported framerates:");
while (fps->num) {
av_log(NULL, AV_LOG_INFO, " %d/%d", fps->num, fps->den);
av_log(NULL, AV_LOG_STDERR, " %d/%d", fps->num, fps->den);
fps++;
}
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "\n");
}
PRINT_CODEC_SUPPORTED(c, pix_fmts, enum AVPixelFormat, "pixel formats",
AV_PIX_FMT_NONE, GET_PIX_FMT_NAME);
@@ -1544,12 +1583,12 @@ static void print_codecs_for_id(enum AVCodecID id, int encoder)
{
const AVCodec *codec = NULL;
av_log(NULL, AV_LOG_INFO, " (%s: ", encoder ? "encoders" : "decoders");
av_log(NULL, AV_LOG_STDERR, " (%s: ", encoder ? "encoders" : "decoders");
while ((codec = next_codec_for_id(id, codec, encoder)))
av_log(NULL, AV_LOG_INFO, "%s ", codec->name);
av_log(NULL, AV_LOG_STDERR, "%s ", codec->name);
av_log(NULL, AV_LOG_INFO, ")");
av_log(NULL, AV_LOG_STDERR, ")");
}
int show_codecs(void *optctx, const char *opt, const char *arg)
@@ -1557,7 +1596,7 @@ int show_codecs(void *optctx, const char *opt, const char *arg)
const AVCodecDescriptor **codecs;
unsigned i, nb_codecs = get_codecs_sorted(&codecs);
av_log(NULL, AV_LOG_INFO, "Codecs:\n"
av_log(NULL, AV_LOG_STDERR, "Codecs:\n"
" D..... = Decoding supported\n"
" .E.... = Encoding supported\n"
" ..V... = Video codec\n"
@@ -1574,16 +1613,16 @@ int show_codecs(void *optctx, const char *opt, const char *arg)
if (strstr(desc->name, "_deprecated"))
continue;
av_log(NULL, AV_LOG_INFO, " ");
av_log(NULL, AV_LOG_INFO, avcodec_find_decoder(desc->id) ? "D" : ".");
av_log(NULL, AV_LOG_INFO, avcodec_find_encoder(desc->id) ? "E" : ".");
av_log(NULL, AV_LOG_STDERR, " ");
av_log(NULL, AV_LOG_STDERR, avcodec_find_decoder(desc->id) ? "D" : ".");
av_log(NULL, AV_LOG_STDERR, avcodec_find_encoder(desc->id) ? "E" : ".");
av_log(NULL, AV_LOG_INFO, "%c", get_media_type_char(desc->type));
av_log(NULL, AV_LOG_INFO, (desc->props & AV_CODEC_PROP_INTRA_ONLY) ? "I" : ".");
av_log(NULL, AV_LOG_INFO, (desc->props & AV_CODEC_PROP_LOSSY) ? "L" : ".");
av_log(NULL, AV_LOG_INFO, (desc->props & AV_CODEC_PROP_LOSSLESS) ? "S" : ".");
av_log(NULL, AV_LOG_STDERR, "%c", get_media_type_char(desc->type));
av_log(NULL, AV_LOG_STDERR, (desc->props & AV_CODEC_PROP_INTRA_ONLY) ? "I" : ".");
av_log(NULL, AV_LOG_STDERR, (desc->props & AV_CODEC_PROP_LOSSY) ? "L" : ".");
av_log(NULL, AV_LOG_STDERR, (desc->props & AV_CODEC_PROP_LOSSLESS) ? "S" : ".");
av_log(NULL, AV_LOG_INFO, " %-20s %s", desc->name, desc->long_name ? desc->long_name : "");
av_log(NULL, AV_LOG_STDERR, " %-20s %s", desc->name, desc->long_name ? desc->long_name : "");
/* print decoders/encoders when there's more than one or their
* names are different from codec name */
@@ -1601,7 +1640,7 @@ int show_codecs(void *optctx, const char *opt, const char *arg)
}
}
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "\n");
}
av_free(codecs);
return 0;
@@ -1612,7 +1651,7 @@ static void print_codecs(int encoder)
const AVCodecDescriptor **codecs;
unsigned i, nb_codecs = get_codecs_sorted(&codecs);
av_log(NULL, AV_LOG_INFO, "%s:\n"
av_log(NULL, AV_LOG_STDERR, "%s:\n"
" V..... = Video\n"
" A..... = Audio\n"
" S..... = Subtitle\n"
@@ -1628,18 +1667,18 @@ static void print_codecs(int encoder)
const AVCodec *codec = NULL;
while ((codec = next_codec_for_id(desc->id, codec, encoder))) {
av_log(NULL, AV_LOG_INFO, " %c", get_media_type_char(desc->type));
av_log(NULL, AV_LOG_INFO, (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) ? "F" : ".");
av_log(NULL, AV_LOG_INFO, (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) ? "S" : ".");
av_log(NULL, AV_LOG_INFO, (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) ? "X" : ".");
av_log(NULL, AV_LOG_INFO, (codec->capabilities & AV_CODEC_CAP_DRAW_HORIZ_BAND)?"B" : ".");
av_log(NULL, AV_LOG_INFO, (codec->capabilities & AV_CODEC_CAP_DR1) ? "D" : ".");
av_log(NULL, AV_LOG_STDERR, " %c", get_media_type_char(desc->type));
av_log(NULL, AV_LOG_STDERR, (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) ? "F" : ".");
av_log(NULL, AV_LOG_STDERR, (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) ? "S" : ".");
av_log(NULL, AV_LOG_STDERR, (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) ? "X" : ".");
av_log(NULL, AV_LOG_STDERR, (codec->capabilities & AV_CODEC_CAP_DRAW_HORIZ_BAND)?"B" : ".");
av_log(NULL, AV_LOG_STDERR, (codec->capabilities & AV_CODEC_CAP_DR1) ? "D" : ".");
av_log(NULL, AV_LOG_INFO, " %-20s %s", codec->name, codec->long_name ? codec->long_name : "");
av_log(NULL, AV_LOG_STDERR, " %-20s %s", codec->name, codec->long_name ? codec->long_name : "");
if (strcmp(codec->name, desc->name))
av_log(NULL, AV_LOG_INFO, " (codec %s)", desc->name);
av_log(NULL, AV_LOG_STDERR, " (codec %s)", desc->name);
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "\n");
}
}
av_free(codecs);
@@ -1662,10 +1701,10 @@ int show_bsfs(void *optctx, const char *opt, const char *arg)
const AVBitStreamFilter *bsf = NULL;
void *opaque = NULL;
av_log(NULL, AV_LOG_INFO, "Bitstream filters:\n");
av_log(NULL, AV_LOG_STDERR, "Bitstream filters:\n");
while ((bsf = av_bsf_iterate(&opaque)))
av_log(NULL, AV_LOG_INFO, "%s\n", bsf->name);
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "%s\n", bsf->name);
av_log(NULL, AV_LOG_STDERR, "\n");
return 0;
}
@@ -1674,13 +1713,13 @@ int show_protocols(void *optctx, const char *opt, const char *arg)
void *opaque = NULL;
const char *name;
av_log(NULL, AV_LOG_INFO, "Supported file protocols:\n"
av_log(NULL, AV_LOG_STDERR, "Supported file protocols:\n"
"Input:\n");
while ((name = avio_enum_protocols(&opaque, 0)))
av_log(NULL, AV_LOG_INFO, " %s\n", name);
av_log(NULL, AV_LOG_INFO, "Output:\n");
av_log(NULL, AV_LOG_STDERR, " %s\n", name);
av_log(NULL, AV_LOG_STDERR, "Output:\n");
while ((name = avio_enum_protocols(&opaque, 1)))
av_log(NULL, AV_LOG_INFO, " %s\n", name);
av_log(NULL, AV_LOG_STDERR, " %s\n", name);
return 0;
}
@@ -1693,7 +1732,7 @@ int show_filters(void *optctx, const char *opt, const char *arg)
int i, j;
const AVFilterPad *pad;
av_log(NULL, AV_LOG_INFO, "Filters:\n"
av_log(NULL, AV_LOG_STDERR, "Filters:\n"
" T.. = Timeline support\n"
" .S. = Slice threading\n"
" ..C = Command support\n"
@@ -1719,14 +1758,14 @@ int show_filters(void *optctx, const char *opt, const char *arg)
( i && (filter->flags & AVFILTER_FLAG_DYNAMIC_OUTPUTS))) ? 'N' : '|';
}
*descr_cur = 0;
av_log(NULL, AV_LOG_INFO, " %c%c%c %-17s %-10s %s\n",
av_log(NULL, AV_LOG_STDERR, " %c%c%c %-17s %-10s %s\n",
filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE ? 'T' : '.',
filter->flags & AVFILTER_FLAG_SLICE_THREADS ? 'S' : '.',
filter->process_command ? 'C' : '.',
filter->name, descr, filter->description);
}
#else
av_log(NULL, AV_LOG_INFO, "No filters available: libavfilter disabled\n");
av_log(NULL, AV_LOG_STDERR, "No filters available: libavfilter disabled\n");
#endif
return 0;
}
@@ -1737,10 +1776,10 @@ int show_colors(void *optctx, const char *opt, const char *arg)
const uint8_t *rgb;
int i;
av_log(NULL, AV_LOG_INFO, "%-32s #RRGGBB\n", "name");
av_log(NULL, AV_LOG_STDERR, "%-32s #RRGGBB\n", "name");
for (i = 0; (name = av_get_known_color_name(i, &rgb)); i++)
av_log(NULL, AV_LOG_INFO, "%-32s #%02x%02x%02x\n", name, rgb[0], rgb[1], rgb[2]);
av_log(NULL, AV_LOG_STDERR, "%-32s #%02x%02x%02x\n", name, rgb[0], rgb[1], rgb[2]);
return 0;
}
@@ -1749,7 +1788,7 @@ int show_pix_fmts(void *optctx, const char *opt, const char *arg)
{
const AVPixFmtDescriptor *pix_desc = NULL;
av_log(NULL, AV_LOG_INFO, "Pixel formats:\n"
av_log(NULL, AV_LOG_STDERR, "Pixel formats:\n"
"I.... = Supported Input format for conversion\n"
".O... = Supported Output format for conversion\n"
"..H.. = Hardware accelerated format\n"
@@ -1765,7 +1804,7 @@ int show_pix_fmts(void *optctx, const char *opt, const char *arg)
while ((pix_desc = av_pix_fmt_desc_next(pix_desc))) {
enum AVPixelFormat av_unused pix_fmt = av_pix_fmt_desc_get_id(pix_desc);
av_log(NULL, AV_LOG_INFO, "%c%c%c%c%c %-16s %d %2d\n",
av_log(NULL, AV_LOG_STDERR, "%c%c%c%c%c %-16s %d %2d\n",
sws_isSupportedInput (pix_fmt) ? 'I' : '.',
sws_isSupportedOutput(pix_fmt) ? 'O' : '.',
pix_desc->flags & AV_PIX_FMT_FLAG_HWACCEL ? 'H' : '.',
@@ -1784,24 +1823,24 @@ int show_layouts(void *optctx, const char *opt, const char *arg)
uint64_t layout, j;
const char *name, *descr;
av_log(NULL, AV_LOG_INFO, "Individual channels:\n"
av_log(NULL, AV_LOG_STDERR, "Individual channels:\n"
"NAME DESCRIPTION\n");
for (i = 0; i < 63; i++) {
name = av_get_channel_name((uint64_t)1 << i);
if (!name)
continue;
descr = av_get_channel_description((uint64_t)1 << i);
av_log(NULL, AV_LOG_INFO, "%-14s %s\n", name, descr);
av_log(NULL, AV_LOG_STDERR, "%-14s %s\n", name, descr);
}
av_log(NULL, AV_LOG_INFO, "\nStandard channel layouts:\n"
av_log(NULL, AV_LOG_STDERR, "\nStandard channel layouts:\n"
"NAME DECOMPOSITION\n");
for (i = 0; !av_get_standard_channel_layout(i, &layout, &name); i++) {
if (name) {
av_log(NULL, AV_LOG_INFO, "%-14s ", name);
av_log(NULL, AV_LOG_STDERR, "%-14s ", name);
for (j = 1; j; j <<= 1)
if ((layout & j))
av_log(NULL, AV_LOG_INFO, "%s%s", (layout & (j - 1)) ? "+" : "", av_get_channel_name(j));
av_log(NULL, AV_LOG_INFO, "\n");
av_log(NULL, AV_LOG_STDERR, "%s%s", (layout & (j - 1)) ? "+" : "", av_get_channel_name(j));
av_log(NULL, AV_LOG_STDERR, "\n");
}
}
return 0;
@@ -1812,7 +1851,7 @@ int show_sample_fmts(void *optctx, const char *opt, const char *arg)
int i;
char fmt_str[128];
for (i = -1; i < AV_SAMPLE_FMT_NB; i++)
av_log(NULL, AV_LOG_INFO, "%s\n", av_get_sample_fmt_string(fmt_str, sizeof(fmt_str), i));
av_log(NULL, AV_LOG_STDERR, "%s\n", av_get_sample_fmt_string(fmt_str, sizeof(fmt_str), i));
return 0;
}
@@ -1860,10 +1899,10 @@ static void show_help_demuxer(const char *name)
return;
}
av_log(NULL, AV_LOG_INFO, "Demuxer %s [%s]:\n", fmt->name, fmt->long_name);
av_log(NULL, AV_LOG_STDERR, "Demuxer %s [%s]:\n", fmt->name, fmt->long_name);
if (fmt->extensions)
av_log(NULL, AV_LOG_INFO, " Common extensions: %s.\n", fmt->extensions);
av_log(NULL, AV_LOG_STDERR, " Common extensions: %s.\n", fmt->extensions);
if (fmt->priv_class)
show_help_children(fmt->priv_class, AV_OPT_FLAG_DECODING_PARAM);
@@ -1879,23 +1918,23 @@ static void show_help_muxer(const char *name)
return;
}
av_log(NULL, AV_LOG_INFO, "Muxer %s [%s]:\n", fmt->name, fmt->long_name);
av_log(NULL, AV_LOG_STDERR, "Muxer %s [%s]:\n", fmt->name, fmt->long_name);
if (fmt->extensions)
av_log(NULL, AV_LOG_INFO, " Common extensions: %s.\n", fmt->extensions);
av_log(NULL, AV_LOG_STDERR, " Common extensions: %s.\n", fmt->extensions);
if (fmt->mime_type)
av_log(NULL, AV_LOG_INFO, " Mime type: %s.\n", fmt->mime_type);
av_log(NULL, AV_LOG_STDERR, " Mime type: %s.\n", fmt->mime_type);
if (fmt->video_codec != AV_CODEC_ID_NONE &&
(desc = avcodec_descriptor_get(fmt->video_codec))) {
av_log(NULL, AV_LOG_INFO, " Default video codec: %s.\n", desc->name);
av_log(NULL, AV_LOG_STDERR, " Default video codec: %s.\n", desc->name);
}
if (fmt->audio_codec != AV_CODEC_ID_NONE &&
(desc = avcodec_descriptor_get(fmt->audio_codec))) {
av_log(NULL, AV_LOG_INFO, " Default audio codec: %s.\n", desc->name);
av_log(NULL, AV_LOG_STDERR, " Default audio codec: %s.\n", desc->name);
}
if (fmt->subtitle_codec != AV_CODEC_ID_NONE &&
(desc = avcodec_descriptor_get(fmt->subtitle_codec))) {
av_log(NULL, AV_LOG_INFO, " Default subtitle codec: %s.\n", desc->name);
av_log(NULL, AV_LOG_STDERR, " Default subtitle codec: %s.\n", desc->name);
}
if (fmt->priv_class)
@@ -1917,40 +1956,40 @@ static void show_help_filter(const char *name)
return;
}
av_log(NULL, AV_LOG_INFO, "Filter %s\n", f->name);
av_log(NULL, AV_LOG_STDERR, "Filter %s\n", f->name);
if (f->description)
av_log(NULL, AV_LOG_INFO, " %s\n", f->description);
av_log(NULL, AV_LOG_STDERR, " %s\n", f->description);
if (f->flags & AVFILTER_FLAG_SLICE_THREADS)
av_log(NULL, AV_LOG_INFO, " slice threading supported\n");
av_log(NULL, AV_LOG_STDERR, " slice threading supported\n");
av_log(NULL, AV_LOG_INFO, " Inputs:\n");
av_log(NULL, AV_LOG_STDERR, " Inputs:\n");
count = avfilter_pad_count(f->inputs);
for (i = 0; i < count; i++) {
av_log(NULL, AV_LOG_INFO, " #%d: %s (%s)\n", i, avfilter_pad_get_name(f->inputs, i),
av_log(NULL, AV_LOG_STDERR, " #%d: %s (%s)\n", i, avfilter_pad_get_name(f->inputs, i),
media_type_string(avfilter_pad_get_type(f->inputs, i)));
}
if (f->flags & AVFILTER_FLAG_DYNAMIC_INPUTS)
av_log(NULL, AV_LOG_INFO, " dynamic (depending on the options)\n");
av_log(NULL, AV_LOG_STDERR, " dynamic (depending on the options)\n");
else if (!count)
av_log(NULL, AV_LOG_INFO, " none (source filter)\n");
av_log(NULL, AV_LOG_STDERR, " none (source filter)\n");
av_log(NULL, AV_LOG_INFO, " Outputs:\n");
av_log(NULL, AV_LOG_STDERR, " Outputs:\n");
count = avfilter_pad_count(f->outputs);
for (i = 0; i < count; i++) {
av_log(NULL, AV_LOG_INFO, " #%d: %s (%s)\n", i, avfilter_pad_get_name(f->outputs, i),
av_log(NULL, AV_LOG_STDERR, " #%d: %s (%s)\n", i, avfilter_pad_get_name(f->outputs, i),
media_type_string(avfilter_pad_get_type(f->outputs, i)));
}
if (f->flags & AVFILTER_FLAG_DYNAMIC_OUTPUTS)
av_log(NULL, AV_LOG_INFO, " dynamic (depending on the options)\n");
av_log(NULL, AV_LOG_STDERR, " dynamic (depending on the options)\n");
else if (!count)
av_log(NULL, AV_LOG_INFO, " none (sink filter)\n");
av_log(NULL, AV_LOG_STDERR, " none (sink filter)\n");
if (f->priv_class)
show_help_children(f->priv_class, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM |
AV_OPT_FLAG_AUDIO_PARAM);
if (f->flags & AVFILTER_FLAG_SUPPORT_TIMELINE)
av_log(NULL, AV_LOG_INFO, "This filter has support for timeline through the 'enable' option.\n");
av_log(NULL, AV_LOG_STDERR, "This filter has support for timeline through the 'enable' option.\n");
#else
av_log(NULL, AV_LOG_ERROR, "Build without libavfilter; "
"can not to satisfy request\n");
@@ -1970,7 +2009,7 @@ static void show_help_bsf(const char *name)
return;
}
av_log(NULL, AV_LOG_INFO, "Bit stream filter %s\n", bsf->name);
av_log(NULL, AV_LOG_STDERR, "Bit stream filter %s\n", bsf->name);
PRINT_CODEC_SUPPORTED(bsf, codec_ids, enum AVCodecID, "codecs",
AV_CODEC_ID_NONE, GET_CODEC_NAME);
if (bsf->priv_class)
@@ -1980,7 +2019,6 @@ static void show_help_bsf(const char *name)
int show_help(void *optctx, const char *opt, const char *arg)
{
char *topic, *par;
av_log_set_callback(mobileffmpeg_log_callback_function);
topic = av_strdup(arg ? arg : "");
if (!topic)
@@ -1990,7 +2028,11 @@ int show_help(void *optctx, const char *opt, const char *arg)
*par++ = 0;
if (!*topic) {
show_help_default(topic, par);
if (program_name && !strcmp(program_name, "ffmpeg")) {
show_help_default_ffmpeg(topic, par);
} else {
show_help_default_ffprobe(topic, par);
}
} else if (!strcmp(topic, "decoder")) {
show_help_codec(par, 0);
} else if (!strcmp(topic, "encoder")) {
@@ -2006,7 +2048,11 @@ int show_help(void *optctx, const char *opt, const char *arg)
} else if (!strcmp(topic, "bsf")) {
show_help_bsf(par);
} else {
show_help_default(topic, par);
if (program_name && !strcmp(program_name, "ffmpeg")) {
show_help_default_ffmpeg(topic, par);
} else {
show_help_default_ffprobe(topic, par);
}
}
av_freep(&topic);
@@ -2038,7 +2084,7 @@ FILE *get_preset_file(char *filename, size_t filename_size,
av_strlcpy(filename, preset_name, filename_size);
f = fopen(filename, "r");
} else {
#ifdef _WIN32
#if HAVE_GETMODULEHANDLE
char datadir[MAX_PATH], *ls;
base[2] = NULL;
@@ -2206,20 +2252,20 @@ static int print_device_sources(AVInputFormat *fmt, AVDictionary *opts)
if (!fmt || !fmt->priv_class || !AV_IS_INPUT_DEVICE(fmt->priv_class->category))
return AVERROR(EINVAL);
av_log(NULL, AV_LOG_INFO, "Auto-detected sources for %s:\n", fmt->name);
av_log(NULL, AV_LOG_STDERR, "Auto-detected sources for %s:\n", fmt->name);
if (!fmt->get_device_list) {
ret = AVERROR(ENOSYS);
av_log(NULL, AV_LOG_INFO, "Cannot list sources. Not implemented.\n");
av_log(NULL, AV_LOG_STDERR, "Cannot list sources. Not implemented.\n");
goto fail;
}
if ((ret = avdevice_list_input_sources(fmt, NULL, opts, &device_list)) < 0) {
av_log(NULL, AV_LOG_INFO, "Cannot list sources.\n");
av_log(NULL, AV_LOG_STDERR, "Cannot list sources.\n");
goto fail;
}
for (i = 0; i < device_list->nb_devices; i++) {
av_log(NULL, AV_LOG_INFO, "%s %s [%s]\n", device_list->default_device == i ? "*" : " ",
av_log(NULL, AV_LOG_STDERR, "%s %s [%s]\n", device_list->default_device == i ? "*" : " ",
device_list->devices[i]->device_name, device_list->devices[i]->device_description);
}
@@ -2236,20 +2282,20 @@ static int print_device_sinks(AVOutputFormat *fmt, AVDictionary *opts)
if (!fmt || !fmt->priv_class || !AV_IS_OUTPUT_DEVICE(fmt->priv_class->category))
return AVERROR(EINVAL);
av_log(NULL, AV_LOG_INFO, "Auto-detected sinks for %s:\n", fmt->name);
av_log(NULL, AV_LOG_STDERR, "Auto-detected sinks for %s:\n", fmt->name);
if (!fmt->get_device_list) {
ret = AVERROR(ENOSYS);
av_log(NULL, AV_LOG_INFO, "Cannot list sinks. Not implemented.\n");
av_log(NULL, AV_LOG_STDERR, "Cannot list sinks. Not implemented.\n");
goto fail;
}
if ((ret = avdevice_list_output_sinks(fmt, NULL, opts, &device_list)) < 0) {
av_log(NULL, AV_LOG_INFO, "Cannot list sinks.\n");
av_log(NULL, AV_LOG_STDERR, "Cannot list sinks.\n");
goto fail;
}
for (i = 0; i < device_list->nb_devices; i++) {
av_log(NULL, AV_LOG_INFO, "%s %s [%s]\n", device_list->default_device == i ? "*" : " ",
av_log(NULL, AV_LOG_STDERR, "%s %s [%s]\n", device_list->default_device == i ? "*" : " ",
device_list->devices[i]->device_name, device_list->devices[i]->device_description);
}
@@ -2275,7 +2321,7 @@ static int show_sinks_sources_parse_arg(const char *arg, char **dev, AVDictionar
}
}
} else
av_log(NULL, AV_LOG_INFO, "\nDevice name is not provided.\n"
av_log(NULL, AV_LOG_STDERR, "\nDevice name is not provided.\n"
"You can pass devicename[,opt1=val1[,opt2=val2...]] as an argument.\n\n");
return 0;
}
+26 -53
View File
@@ -20,6 +20,13 @@
*/
/*
* CHANGES 01.2020
* - ffprobe support changes
* - AV_LOG_STDERR introduced
*
* CHANGES 12.2019
* - Concurrent execution support
*
* CHANGES 03.2019
* --------------------------------------------------------
* - config.h include removed
@@ -48,22 +55,29 @@
#undef main /* We don't want SDL to override our main() */
#endif
/**
* Defines logs printed to stderr by ffmpeg. They are not filtered and always redirected.
*/
#define AV_LOG_STDERR -16
/**
* program name, defined by the program for show_version().
*/
extern const char program_name[];
extern __thread char *program_name;
/**
* program birth year, defined by the program for show_banner()
*/
extern const int program_birth_year;
extern __thread int program_birth_year;
extern AVCodecContext *avcodec_opts[AVMEDIA_TYPE_NB];
extern AVFormatContext *avformat_opts;
extern AVDictionary *sws_dict;
extern AVDictionary *swr_opts;
extern AVDictionary *format_opts, *codec_opts, *resample_opts;
extern int hide_banner;
extern __thread AVCodecContext *avcodec_opts[AVMEDIA_TYPE_NB];
extern __thread AVFormatContext *avformat_opts;
extern __thread AVDictionary *sws_dict;
extern __thread AVDictionary *swr_opts;
extern __thread AVDictionary *format_opts, *codec_opts, *resample_opts;
extern __thread int hide_banner;
extern __thread int find_stream_info;
extern __thread int filter_nbthreads;
/**
* Register a program-specific cleanup routine.
@@ -113,7 +127,7 @@ int opt_default(void *optctx, const char *opt, const char *arg);
*/
int opt_loglevel(void *optctx, const char *opt, const char *arg);
int opt_report(const char *opt);
int opt_report(void *optctx, const char *opt, const char *arg);
int opt_max_alloc(void *optctx, const char *opt, const char *arg);
@@ -215,47 +229,6 @@ typedef struct OptionDef {
void show_help_options(const OptionDef *options, const char *msg, int req_flags,
int rej_flags, int alt_flags);
#if CONFIG_AVDEVICE
#define CMDUTILS_COMMON_OPTIONS_AVDEVICE \
{ "sources" , OPT_EXIT | HAS_ARG, { .func_arg = show_sources }, \
"list sources of the input device", "device" }, \
{ "sinks" , OPT_EXIT | HAS_ARG, { .func_arg = show_sinks }, \
"list sinks of the output device", "device" }, \
#else
#define CMDUTILS_COMMON_OPTIONS_AVDEVICE
#endif
#define CMDUTILS_COMMON_OPTIONS \
{ "L", OPT_EXIT, { .func_arg = show_license }, "show license" }, \
{ "h", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \
{ "?", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \
{ "help", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \
{ "-help", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \
{ "version", OPT_EXIT, { .func_arg = show_version }, "show version" }, \
{ "buildconf", OPT_EXIT, { .func_arg = show_buildconf }, "show build configuration" }, \
{ "formats", OPT_EXIT, { .func_arg = show_formats }, "show available formats" }, \
{ "muxers", OPT_EXIT, { .func_arg = show_muxers }, "show available muxers" }, \
{ "demuxers", OPT_EXIT, { .func_arg = show_demuxers }, "show available demuxers" }, \
{ "devices", OPT_EXIT, { .func_arg = show_devices }, "show available devices" }, \
{ "codecs", OPT_EXIT, { .func_arg = show_codecs }, "show available codecs" }, \
{ "decoders", OPT_EXIT, { .func_arg = show_decoders }, "show available decoders" }, \
{ "encoders", OPT_EXIT, { .func_arg = show_encoders }, "show available encoders" }, \
{ "bsfs", OPT_EXIT, { .func_arg = show_bsfs }, "show available bit stream filters" }, \
{ "protocols", OPT_EXIT, { .func_arg = show_protocols }, "show available protocols" }, \
{ "filters", OPT_EXIT, { .func_arg = show_filters }, "show available filters" }, \
{ "pix_fmts", OPT_EXIT, { .func_arg = show_pix_fmts }, "show available pixel formats" }, \
{ "layouts", OPT_EXIT, { .func_arg = show_layouts }, "show standard channel layouts" }, \
{ "sample_fmts", OPT_EXIT, { .func_arg = show_sample_fmts }, "show available audio sample formats" }, \
{ "colors", OPT_EXIT, { .func_arg = show_colors }, "show available color names" }, \
{ "loglevel", HAS_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" }, \
{ "v", HAS_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" }, \
{ "report", 0, { (void*)opt_report }, "generate a report" }, \
{ "max_alloc", HAS_ARG, { .func_arg = opt_max_alloc }, "set maximum size of a single allocated block", "bytes" }, \
{ "cpuflags", HAS_ARG | OPT_EXPERT, { .func_arg = opt_cpuflags }, "force specific cpu flags", "flags" }, \
{ "hide_banner", OPT_BOOL | OPT_EXPERT, {&hide_banner}, "do not show program banner", "hide_banner" }, \
CMDUTILS_COMMON_OPTIONS_AVDEVICE \
/**
* Show help for all options with given flags in class and all its
* children.
@@ -263,10 +236,11 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags,
void show_help_children(const AVClass *class, int flags);
/**
* Per-fftool specific help handler. Implemented in each
* Per-fftool specific help handlers. Implemented in each
* fftool, called by show_help().
*/
void show_help_default(const char *opt, const char *arg);
void show_help_default_ffmpeg(const char *opt, const char *arg);
void show_help_default_ffprobe(const char *opt, const char *arg);
/**
* Generic -h handler common to all fftools.
@@ -361,7 +335,6 @@ typedef struct OptionParseContext {
* Parse an options group and write results into optctx.
*
* @param optctx an app-specific options context. NULL for global options group
* @param g option group
*/
int parse_optgroup(void *optctx, OptionGroup *g);
+541 -63
View File
@@ -24,6 +24,12 @@
*/
/*
* CHANGES 01.2020
* - ffprobe support changes
*
* CHANGES 12.2019
* - Concurrent execution support
*
* CHANGES 08.2018
* --------------------------------------------------------
* - fftools_ prefix added to file name and parent headers
@@ -35,10 +41,10 @@
*
* CHANGES 07.2018
* --------------------------------------------------------
* - main() function renamed as execute()
* - main() function renamed as ffmpeg_execute()
* - exit_program() implemented with setjmp
* - extern longjmp_value added to access exit code stored in exit_program()
* - cleanup() method added
* - ffmpeg_var_cleanup() method added
*/
#include "config.h"
@@ -126,9 +132,6 @@
#include "libavutil/avassert.h"
const char program_name[] = "ffmpeg";
const int program_birth_year = 2000;
static FILE *vstats_file;
const char *const forced_keyframes_const_names[] = {
@@ -151,39 +154,85 @@ static BenchmarkTimeStamps get_benchmark_time_stamps(void);
static int64_t getmaxrss(void);
static int ifilter_has_all_input_formats(FilterGraph *fg);
static int run_as_daemon = 0;
static int nb_frames_dup = 0;
static unsigned dup_warning = 1000;
static int nb_frames_drop = 0;
static int64_t decode_error_stat[2];
__thread int run_as_daemon = 0;
__thread int nb_frames_dup = 0;
__thread unsigned dup_warning = 1000;
__thread int nb_frames_drop = 0;
__thread int64_t decode_error_stat[2];
static int want_sdp = 1;
__thread int want_sdp = 1;
static BenchmarkTimeStamps current_time;
AVIOContext *progress_avio = NULL;
__thread BenchmarkTimeStamps current_time;
__thread AVIOContext *progress_avio = NULL;
static uint8_t *subtitle_out;
__thread uint8_t *subtitle_out;
InputStream **input_streams = NULL;
int nb_input_streams = 0;
InputFile **input_files = NULL;
int nb_input_files = 0;
__thread InputStream **input_streams = NULL;
__thread int nb_input_streams = 0;
__thread InputFile **input_files = NULL;
__thread int nb_input_files = 0;
OutputStream **output_streams = NULL;
int nb_output_streams = 0;
OutputFile **output_files = NULL;
int nb_output_files = 0;
__thread OutputStream **output_streams = NULL;
__thread int nb_output_streams = 0;
__thread OutputFile **output_files = NULL;
__thread int nb_output_files = 0;
FilterGraph **filtergraphs;
int nb_filtergraphs;
__thread FilterGraph **filtergraphs;
__thread int nb_filtergraphs;
static void (*report_callback)(int, float, float, int64_t, int, double, double) = NULL;
void (*report_callback)(int, float, float, int64_t, int, double, double) = NULL;
extern __thread int file_overwrite;
extern __thread int no_file_overwrite;
extern __thread int ignore_unknown_streams;
extern __thread int copy_unknown_streams;
extern int opt_map(void *optctx, const char *opt, const char *arg);
extern int opt_map_channel(void *optctx, const char *opt, const char *arg);
extern int opt_recording_timestamp(void *optctx, const char *opt, const char *arg);
extern int opt_data_frames(void *optctx, const char *opt, const char *arg);
extern int opt_progress(void *optctx, const char *opt, const char *arg);
extern int opt_target(void *optctx, const char *opt, const char *arg);
extern int opt_vsync(void *optctx, const char *opt, const char *arg);
extern int opt_abort_on(void *optctx, const char *opt, const char *arg);
extern int opt_qscale(void *optctx, const char *opt, const char *arg);
extern int opt_profile(void *optctx, const char *opt, const char *arg);
extern int opt_filter_complex(void *optctx, const char *opt, const char *arg);
extern int opt_filter_complex_script(void *optctx, const char *opt, const char *arg);
extern int opt_attach(void *optctx, const char *opt, const char *arg);
extern int opt_video_frames(void *optctx, const char *opt, const char *arg);
extern __thread int intra_only;
extern int opt_video_codec(void *optctx, const char *opt, const char *arg);
extern int opt_sameq(void *optctx, const char *opt, const char *arg);
extern int opt_timecode(void *optctx, const char *opt, const char *arg);
extern __thread int do_psnr;
extern int opt_vstats_file(void *optctx, const char *opt, const char *arg);
extern int opt_vstats(void *optctx, const char *opt, const char *arg);
extern int opt_video_frames(void *optctx, const char *opt, const char *arg);
extern int opt_old2new(void *optctx, const char *opt, const char *arg);
extern int opt_streamid(void *optctx, const char *opt, const char *arg);
extern int opt_bitrate(void *optctx, const char *opt, const char *arg);
extern int show_hwaccels(void *optctx, const char *opt, const char *arg);
extern int opt_video_filters(void *optctx, const char *opt, const char *arg);
extern int opt_audio_frames(void *optctx, const char *opt, const char *arg);
extern int opt_audio_qscale(void *optctx, const char *opt, const char *arg);
extern int opt_audio_codec(void *optctx, const char *opt, const char *arg);
extern int opt_channel_layout(void *optctx, const char *opt, const char *arg);
extern int opt_preset(void *optctx, const char *opt, const char *arg);
extern int opt_audio_filters(void *optctx, const char *opt, const char *arg);
extern int opt_subtitle_codec(void *optctx, const char *opt, const char *arg);
extern int opt_video_channel(void *optctx, const char *opt, const char *arg);
extern int opt_video_standard(void *optctx, const char *opt, const char *arg);
extern int opt_sdp_file(void *optctx, const char *opt, const char *arg);
extern int opt_data_codec(void *optctx, const char *opt, const char *arg);
extern int opt_init_hw_device(void *optctx, const char *opt, const char *arg);
extern int opt_filter_hw_device(void *optctx, const char *opt, const char *arg);
extern __thread int input_sync;
#if HAVE_TERMIOS_H
/* init terminal so that we can grab keys */
static struct termios oldtty;
static int restore_tty;
__thread struct termios oldtty;
__thread int restore_tty;
#endif
#if HAVE_THREADS
@@ -353,12 +402,12 @@ void term_exit(void)
term_exit_sigsafe();
}
static volatile int received_sigterm = 0;
static volatile int received_nb_signals = 0;
static atomic_int transcode_init_done = ATOMIC_VAR_INIT(0);
static volatile int ffmpeg_exited = 0;
static int main_return_code = 0;
extern int longjmp_value;
volatile int received_sigterm = 0;
volatile int received_nb_signals = 0;
__thread atomic_int transcode_init_done = ATOMIC_VAR_INIT(0);
__thread volatile int ffmpeg_exited = 0;
__thread int main_ffmpeg_return_code = 0;
extern __thread int longjmp_value;
static void
sigterm_handler(int sig)
@@ -496,12 +545,14 @@ static int read_key(void)
return -1;
}
static int decode_interrupt_cb(void *ctx)
int decode_interrupt_cb(void *ctx);
int decode_interrupt_cb(void *ctx)
{
return received_nb_signals > atomic_load(&transcode_init_done);
}
const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL };
__thread const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL };
static void ffmpeg_cleanup(int ret)
{
@@ -590,6 +641,7 @@ static void ffmpeg_cleanup(int ret)
ost->audio_channels_mapped = 0;
av_dict_free(&ost->sws_dict);
av_dict_free(&ost->swr_opts);
avcodec_free_context(&ost->enc_ctx);
avcodec_parameters_free(&ost->ref_par);
@@ -838,7 +890,7 @@ static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int u
ret = av_interleaved_write_frame(s, pkt);
if (ret < 0) {
print_error("av_interleaved_write_frame()", ret);
main_return_code = 1;
main_ffmpeg_return_code = 1;
close_all_output_streams(ost, MUXER_FINISHED | ENCODER_FINISHED, ENCODER_FINISHED);
}
av_packet_unref(pkt);
@@ -2103,12 +2155,13 @@ static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *p
InputFile *f = input_files [ist->file_index];
int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time;
int64_t ost_tb_start_time = av_rescale_q(start_time, AV_TIME_BASE_Q, ost->mux_timebase);
AVPacket opkt = { 0 };
av_init_packet(&opkt);
AVPacket opkt;
// EOF: flush output bitstream filters.
if (!pkt) {
av_init_packet(&opkt);
opkt.data = NULL;
opkt.size = 0;
output_packet(of, &opkt, ost, 1);
return;
}
@@ -2147,10 +2200,11 @@ static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *p
if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
ost->sync_opts++;
if (av_packet_ref(&opkt, pkt) < 0)
exit_program(1);
if (pkt->pts != AV_NOPTS_VALUE)
opkt.pts = av_rescale_q(pkt->pts, ist->st->time_base, ost->mux_timebase) - ost_tb_start_time;
else
opkt.pts = AV_NOPTS_VALUE;
if (pkt->dts == AV_NOPTS_VALUE)
opkt.dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ost->mux_timebase);
@@ -2169,18 +2223,6 @@ static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *p
opkt.duration = av_rescale_q(pkt->duration, ist->st->time_base, ost->mux_timebase);
opkt.flags = pkt->flags;
if (pkt->buf) {
opkt.buf = av_buffer_ref(pkt->buf);
if (!opkt.buf)
exit_program(1);
}
opkt.data = pkt->data;
opkt.size = pkt->size;
av_copy_packet_side_data(&opkt, pkt);
output_packet(of, &opkt, ost, 0);
}
@@ -2890,7 +2932,7 @@ static void print_sdp(void)
av_sdp_create(avc, j, sdp, sizeof(sdp));
if (!sdp_filename) {
av_log(NULL, AV_LOG_INFO, "SDP:\n%s\n", sdp);
av_log(NULL, AV_LOG_STDERR, "SDP:\n%s\n", sdp);
fflush(stdout);
} else {
if (avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL) < 0) {
@@ -3987,7 +4029,9 @@ static OutputStream *choose_output(void)
av_rescale_q(ost->st->cur_dts, ost->st->time_base,
AV_TIME_BASE_Q);
if (ost->st->cur_dts == AV_NOPTS_VALUE)
av_log(NULL, AV_LOG_DEBUG, "cur_dts is invalid (this is harmless if it occurs once at the start per stream)\n");
av_log(NULL, AV_LOG_DEBUG,
"cur_dts is invalid st:%d (%d) [init:%d i_done:%d finish:%d] (this is harmless if it occurs once at the start per stream)\n",
ost->st->index, ost->st->id, ost->initialized, ost->inputs_done, ost->finished);
if (!ost->initialized && !ost->inputs_done)
return ost;
@@ -4305,7 +4349,7 @@ static int seek_to_start(InputFile *ifile, AVFormatContext *is)
int i, ret, has_audio = 0;
int64_t duration = 0;
ret = av_seek_frame(is, -1, is->start_time, 0);
ret = avformat_seek_file(is, -1, INT64_MIN, is->start_time, is->start_time, 0);
if (ret < 0)
return ret;
@@ -4950,8 +4994,8 @@ static void log_callback_null(void *ptr, int level, const char *fmt, va_list vl)
{
}
void cleanup() {
main_return_code = 0;
void ffmpeg_var_cleanup() {
main_ffmpeg_return_code = 0;
longjmp_value = 0;
received_sigterm = 0;
received_nb_signals = 0;
@@ -4990,15 +5034,449 @@ void cancel_operation()
sigterm_handler(SIGINT);
}
int execute(int argc, char **argv)
__thread OptionDef *ffmpeg_options = NULL;
int ffmpeg_execute(int argc, char **argv)
{
char _program_name[] = "ffmpeg";
program_name = (char*)&_program_name;
program_birth_year = 2000;
#define OFFSET(x) offsetof(OptionsContext, x)
OptionDef options[] = {
/* main options */
{ "L", OPT_EXIT, { .func_arg = show_license }, "show license" },
{ "h", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" },
{ "?", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" },
{ "help", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" },
{ "-help", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" },
{ "version", OPT_EXIT, { .func_arg = show_version }, "show version" },
{ "buildconf", OPT_EXIT, { .func_arg = show_buildconf }, "show build configuration" },
{ "formats", OPT_EXIT, { .func_arg = show_formats }, "show available formats" },
{ "muxers", OPT_EXIT, { .func_arg = show_muxers }, "show available muxers" },
{ "demuxers", OPT_EXIT, { .func_arg = show_demuxers }, "show available demuxers" },
{ "devices", OPT_EXIT, { .func_arg = show_devices }, "show available devices" },
{ "codecs", OPT_EXIT, { .func_arg = show_codecs }, "show available codecs" },
{ "decoders", OPT_EXIT, { .func_arg = show_decoders }, "show available decoders" },
{ "encoders", OPT_EXIT, { .func_arg = show_encoders }, "show available encoders" },
{ "bsfs", OPT_EXIT, { .func_arg = show_bsfs }, "show available bit stream filters" },
{ "protocols", OPT_EXIT, { .func_arg = show_protocols }, "show available protocols" },
{ "filters", OPT_EXIT, { .func_arg = show_filters }, "show available filters" },
{ "pix_fmts", OPT_EXIT, { .func_arg = show_pix_fmts }, "show available pixel formats" },
{ "layouts", OPT_EXIT, { .func_arg = show_layouts }, "show standard channel layouts" },
{ "sample_fmts", OPT_EXIT, { .func_arg = show_sample_fmts }, "show available audio sample formats" },
{ "colors", OPT_EXIT, { .func_arg = show_colors }, "show available color names" },
{ "loglevel", HAS_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" },
{ "v", HAS_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" },
{ "report", 0, { .func_arg = opt_report }, "generate a report" },
{ "max_alloc", HAS_ARG, { .func_arg = opt_max_alloc }, "set maximum size of a single allocated block", "bytes" },
{ "cpuflags", HAS_ARG | OPT_EXPERT, { .func_arg = opt_cpuflags }, "force specific cpu flags", "flags" },
{ "hide_banner", OPT_BOOL | OPT_EXPERT, {&hide_banner}, "do not show program banner", "hide_banner" },
#if CONFIG_AVDEVICE
{ "sources" , OPT_EXIT | HAS_ARG, { .func_arg = show_sources },
"list sources of the input device", "device" },
{ "sinks" , OPT_EXIT | HAS_ARG, { .func_arg = show_sinks },
"list sinks of the output device", "device" },
#endif
{ "f", HAS_ARG | OPT_STRING | OPT_OFFSET |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(format) },
"force format", "fmt" },
{ "y", OPT_BOOL, { &file_overwrite },
"overwrite output files" },
{ "n", OPT_BOOL, { &no_file_overwrite },
"never overwrite output files" },
{ "ignore_unknown", OPT_BOOL, { &ignore_unknown_streams },
"Ignore unknown stream types" },
{ "copy_unknown", OPT_BOOL | OPT_EXPERT, { &copy_unknown_streams },
"Copy unknown stream types" },
{ "c", HAS_ARG | OPT_STRING | OPT_SPEC |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(codec_names) },
"codec name", "codec" },
{ "codec", HAS_ARG | OPT_STRING | OPT_SPEC |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(codec_names) },
"codec name", "codec" },
{ "pre", HAS_ARG | OPT_STRING | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(presets) },
"preset name", "preset" },
{ "map", HAS_ARG | OPT_EXPERT | OPT_PERFILE |
OPT_OUTPUT, { .func_arg = opt_map },
"set input stream mapping",
"[-]input_file_id[:stream_specifier][,sync_file_id[:stream_specifier]]" },
{ "map_channel", HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_map_channel },
"map an audio channel from one stream to another", "file.stream.channel[:syncfile.syncstream]" },
{ "map_metadata", HAS_ARG | OPT_STRING | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(metadata_map) },
"set metadata information of outfile from infile",
"outfile[,metadata]:infile[,metadata]" },
{ "map_chapters", HAS_ARG | OPT_INT | OPT_EXPERT | OPT_OFFSET |
OPT_OUTPUT, { .off = OFFSET(chapters_input_file) },
"set chapters mapping", "input_file_index" },
{ "t", HAS_ARG | OPT_TIME | OPT_OFFSET |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(recording_time) },
"record or transcode \"duration\" seconds of audio/video",
"duration" },
{ "to", HAS_ARG | OPT_TIME | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(stop_time) },
"record or transcode stop time", "time_stop" },
{ "fs", HAS_ARG | OPT_INT64 | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(limit_filesize) },
"set the limit file size in bytes", "limit_size" },
{ "ss", HAS_ARG | OPT_TIME | OPT_OFFSET |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(start_time) },
"set the start time offset", "time_off" },
{ "sseof", HAS_ARG | OPT_TIME | OPT_OFFSET |
OPT_INPUT, { .off = OFFSET(start_time_eof) },
"set the start time offset relative to EOF", "time_off" },
{ "seek_timestamp", HAS_ARG | OPT_INT | OPT_OFFSET |
OPT_INPUT, { .off = OFFSET(seek_timestamp) },
"enable/disable seeking by timestamp with -ss" },
{ "accurate_seek", OPT_BOOL | OPT_OFFSET | OPT_EXPERT |
OPT_INPUT, { .off = OFFSET(accurate_seek) },
"enable/disable accurate seeking with -ss" },
{ "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET |
OPT_EXPERT | OPT_INPUT, { .off = OFFSET(input_ts_offset) },
"set the input ts offset", "time_off" },
{ "itsscale", HAS_ARG | OPT_DOUBLE | OPT_SPEC |
OPT_EXPERT | OPT_INPUT, { .off = OFFSET(ts_scale) },
"set the input ts scale", "scale" },
{ "timestamp", HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_recording_timestamp },
"set the recording timestamp ('now' to set the current time)", "time" },
{ "metadata", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(metadata) },
"add metadata", "string=string" },
{ "program", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(program) },
"add program with specified streams", "title=string:st=number..." },
{ "dframes", HAS_ARG | OPT_PERFILE | OPT_EXPERT |
OPT_OUTPUT, { .func_arg = opt_data_frames },
"set the number of data frames to output", "number" },
{ "benchmark", OPT_BOOL | OPT_EXPERT, { &do_benchmark },
"add timings for benchmarking" },
{ "benchmark_all", OPT_BOOL | OPT_EXPERT, { &do_benchmark_all },
"add timings for each task" },
{ "progress", HAS_ARG | OPT_EXPERT, { .func_arg = opt_progress },
"write program-readable progress information", "url" },
{ "stdin", OPT_BOOL | OPT_EXPERT, { &stdin_interaction },
"enable or disable interaction on standard input" },
{ "timelimit", HAS_ARG | OPT_EXPERT, { .func_arg = opt_timelimit },
"set max runtime in seconds in CPU user time", "limit" },
{ "dump", OPT_BOOL | OPT_EXPERT, { &do_pkt_dump },
"dump each input packet" },
{ "hex", OPT_BOOL | OPT_EXPERT, { &do_hex_dump },
"when dumping packets, also dump the payload" },
{ "re", OPT_BOOL | OPT_EXPERT | OPT_OFFSET |
OPT_INPUT, { .off = OFFSET(rate_emu) },
"read input at native frame rate", "" },
{ "target", HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_target },
"specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\" or \"dv50\" "
"with optional prefixes \"pal-\", \"ntsc-\" or \"film-\")", "type" },
{ "vsync", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vsync },
"video sync method", "" },
{ "frame_drop_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &frame_drop_threshold },
"frame drop threshold", "" },
{ "async", HAS_ARG | OPT_INT | OPT_EXPERT, { &audio_sync_method },
"audio sync method", "" },
{ "adrift_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &audio_drift_threshold },
"audio drift threshold", "threshold" },
{ "copyts", OPT_BOOL | OPT_EXPERT, { &copy_ts },
"copy timestamps" },
{ "start_at_zero", OPT_BOOL | OPT_EXPERT, { &start_at_zero },
"shift input timestamps to start at 0 when using copyts" },
{ "copytb", HAS_ARG | OPT_INT | OPT_EXPERT, { &copy_tb },
"copy input stream time base when stream copying", "mode" },
{ "shortest", OPT_BOOL | OPT_EXPERT | OPT_OFFSET |
OPT_OUTPUT, { .off = OFFSET(shortest) },
"finish encoding within shortest input" },
{ "bitexact", OPT_BOOL | OPT_EXPERT | OPT_OFFSET |
OPT_OUTPUT | OPT_INPUT, { .off = OFFSET(bitexact) },
"bitexact mode" },
{ "apad", OPT_STRING | HAS_ARG | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(apad) },
"audio pad", "" },
{ "dts_delta_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &dts_delta_threshold },
"timestamp discontinuity delta threshold", "threshold" },
{ "dts_error_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &dts_error_threshold },
"timestamp error delta threshold", "threshold" },
{ "xerror", OPT_BOOL | OPT_EXPERT, { &exit_on_error },
"exit on error", "error" },
{ "abort_on", HAS_ARG | OPT_EXPERT, { .func_arg = opt_abort_on },
"abort on the specified condition flags", "flags" },
{ "copyinkf", OPT_BOOL | OPT_EXPERT | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(copy_initial_nonkeyframes) },
"copy initial non-keyframes" },
{ "copypriorss", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(copy_prior_start) },
"copy or discard frames before start time" },
{ "frames", OPT_INT64 | HAS_ARG | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(max_frames) },
"set the number of frames to output", "number" },
{ "tag", OPT_STRING | HAS_ARG | OPT_SPEC |
OPT_EXPERT | OPT_OUTPUT | OPT_INPUT, { .off = OFFSET(codec_tags) },
"force codec tag/fourcc", "fourcc/tag" },
{ "q", HAS_ARG | OPT_EXPERT | OPT_DOUBLE |
OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(qscale) },
"use fixed quality scale (VBR)", "q" },
{ "qscale", HAS_ARG | OPT_EXPERT | OPT_PERFILE |
OPT_OUTPUT, { .func_arg = opt_qscale },
"use fixed quality scale (VBR)", "q" },
{ "profile", HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_profile },
"set profile", "profile" },
{ "filter", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(filters) },
"set stream filtergraph", "filter_graph" },
{ "filter_threads", HAS_ARG | OPT_INT, { &filter_nbthreads },
"number of non-complex filter threads" },
{ "filter_script", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(filter_scripts) },
"read stream filtergraph description from a file", "filename" },
{ "reinit_filter", HAS_ARG | OPT_INT | OPT_SPEC | OPT_INPUT, { .off = OFFSET(reinit_filters) },
"reinit filtergraph on input parameter changes", "" },
{ "filter_complex", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_complex },
"create a complex filtergraph", "graph_description" },
{ "filter_complex_threads", HAS_ARG | OPT_INT, { &filter_complex_nbthreads },
"number of threads for -filter_complex" },
{ "lavfi", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_complex },
"create a complex filtergraph", "graph_description" },
{ "filter_complex_script", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_complex_script },
"read complex filtergraph description from a file", "filename" },
{ "stats", OPT_BOOL, { &print_stats },
"print progress report during encoding", },
{ "attach", HAS_ARG | OPT_PERFILE | OPT_EXPERT |
OPT_OUTPUT, { .func_arg = opt_attach },
"add an attachment to the output file", "filename" },
{ "dump_attachment", HAS_ARG | OPT_STRING | OPT_SPEC |
OPT_EXPERT | OPT_INPUT, { .off = OFFSET(dump_attachment) },
"extract an attachment into a file", "filename" },
{ "stream_loop", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_INPUT |
OPT_OFFSET, { .off = OFFSET(loop) }, "set number of times input stream shall be looped", "loop count" },
{ "debug_ts", OPT_BOOL | OPT_EXPERT, { &debug_ts },
"print timestamp debugging info" },
{ "max_error_rate", HAS_ARG | OPT_FLOAT, { &max_error_rate },
"ratio of errors (0.0: no errors, 1.0: 100% errors) above which ffmpeg returns an error instead of success.", "maximum error rate" },
{ "discard", OPT_STRING | HAS_ARG | OPT_SPEC |
OPT_INPUT, { .off = OFFSET(discard) },
"discard", "" },
{ "disposition", OPT_STRING | HAS_ARG | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(disposition) },
"disposition", "" },
{ "thread_queue_size", HAS_ARG | OPT_INT | OPT_OFFSET | OPT_EXPERT | OPT_INPUT,
{ .off = OFFSET(thread_queue_size) },
"set the maximum number of queued packets from the demuxer" },
{ "find_stream_info", OPT_BOOL | OPT_PERFILE | OPT_INPUT | OPT_EXPERT, { &find_stream_info },
"read and decode the streams to fill missing information with heuristics" },
/* video options */
{ "vframes", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_frames },
"set the number of video frames to output", "number" },
{ "r", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(frame_rates) },
"set frame rate (Hz value, fraction or abbreviation)", "rate" },
{ "s", OPT_VIDEO | HAS_ARG | OPT_SUBTITLE | OPT_STRING | OPT_SPEC |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(frame_sizes) },
"set frame size (WxH or abbreviation)", "size" },
{ "aspect", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(frame_aspect_ratios) },
"set aspect ratio (4:3, 16:9 or 1.3333, 1.7777)", "aspect" },
{ "pix_fmt", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(frame_pix_fmts) },
"set pixel format", "format" },
{ "bits_per_raw_sample", OPT_VIDEO | OPT_INT | HAS_ARG, { &frame_bits_per_raw_sample },
"set the number of bits per raw sample", "number" },
{ "intra", OPT_VIDEO | OPT_BOOL | OPT_EXPERT, { &intra_only },
"deprecated use -g 1" },
{ "vn", OPT_VIDEO | OPT_BOOL | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,{ .off = OFFSET(video_disable) },
"disable video" },
{ "rc_override", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(rc_overrides) },
"rate control override for specific intervals", "override" },
{ "vcodec", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_INPUT |
OPT_OUTPUT, { .func_arg = opt_video_codec },
"force video codec ('copy' to copy stream)", "codec" },
{ "sameq", OPT_VIDEO | OPT_EXPERT , { .func_arg = opt_sameq },
"Removed" },
{ "same_quant", OPT_VIDEO | OPT_EXPERT , { .func_arg = opt_sameq },
"Removed" },
{ "timecode", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_timecode },
"set initial TimeCode value.", "hh:mm:ss[:;.]ff" },
{ "pass", OPT_VIDEO | HAS_ARG | OPT_SPEC | OPT_INT | OPT_OUTPUT, { .off = OFFSET(pass) },
"select the pass number (1 to 3)", "n" },
{ "passlogfile", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(passlogfiles) },
"select two pass log file name prefix", "prefix" },
{ "deinterlace", OPT_VIDEO | OPT_BOOL | OPT_EXPERT, { &do_deinterlace },
"this option is deprecated, use the yadif filter instead" },
{ "psnr", OPT_VIDEO | OPT_BOOL | OPT_EXPERT, { &do_psnr },
"calculate PSNR of compressed frames" },
{ "vstats", OPT_VIDEO | OPT_EXPERT , { .func_arg = opt_vstats },
"dump video coding statistics to file" },
{ "vstats_file", OPT_VIDEO | HAS_ARG | OPT_EXPERT , { .func_arg = opt_vstats_file },
"dump video coding statistics to file", "file" },
{ "vstats_version", OPT_VIDEO | OPT_INT | HAS_ARG | OPT_EXPERT , { &vstats_version },
"Version of the vstats format to use."},
{ "vf", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_filters },
"set video filters", "filter_graph" },
{ "intra_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(intra_matrices) },
"specify intra matrix coeffs", "matrix" },
{ "inter_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(inter_matrices) },
"specify inter matrix coeffs", "matrix" },
{ "chroma_intra_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(chroma_intra_matrices) },
"specify intra matrix coeffs", "matrix" },
{ "top", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_INT| OPT_SPEC |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(top_field_first) },
"top=1/bottom=0/auto=-1 field first", "" },
{ "vtag", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_PERFILE |
OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_old2new },
"force video tag/fourcc", "fourcc/tag" },
{ "qphist", OPT_VIDEO | OPT_BOOL | OPT_EXPERT , { &qp_hist },
"show QP histogram" },
{ "force_fps", OPT_VIDEO | OPT_BOOL | OPT_EXPERT | OPT_SPEC |
OPT_OUTPUT, { .off = OFFSET(force_fps) },
"force the selected framerate, disable the best supported framerate selection" },
{ "streamid", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_PERFILE |
OPT_OUTPUT, { .func_arg = opt_streamid },
"set the value of an outfile streamid", "streamIndex:value" },
{ "force_key_frames", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(forced_key_frames) },
"force key frames at specified timestamps", "timestamps" },
{ "ab", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate },
"audio bitrate (please use -b:a)", "bitrate" },
{ "b", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate },
"video bitrate (please use -b:v)", "bitrate" },
{ "hwaccel", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccels) },
"use HW accelerated decoding", "hwaccel name" },
{ "hwaccel_device", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccel_devices) },
"select a device for HW acceleration", "devicename" },
{ "hwaccel_output_format", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccel_output_formats) },
"select output format used with HW accelerated decoding", "format" },
#if CONFIG_VIDEOTOOLBOX
{ "videotoolbox_pixfmt", HAS_ARG | OPT_STRING | OPT_EXPERT, { &videotoolbox_pixfmt}, "" },
#endif
{ "hwaccels", OPT_EXIT, { .func_arg = show_hwaccels },
"show available HW acceleration methods" },
{ "autorotate", HAS_ARG | OPT_BOOL | OPT_SPEC |
OPT_EXPERT | OPT_INPUT, { .off = OFFSET(autorotate) },
"automatically insert correct rotate filters" },
/* audio options */
{ "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames },
"set the number of audio frames to output", "number" },
{ "aq", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_qscale },
"set audio quality (codec-specific)", "quality", },
{ "ar", OPT_AUDIO | HAS_ARG | OPT_INT | OPT_SPEC |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(audio_sample_rate) },
"set audio sampling rate (in Hz)", "rate" },
{ "ac", OPT_AUDIO | HAS_ARG | OPT_INT | OPT_SPEC |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(audio_channels) },
"set number of audio channels", "channels" },
{ "an", OPT_AUDIO | OPT_BOOL | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,{ .off = OFFSET(audio_disable) },
"disable audio" },
{ "acodec", OPT_AUDIO | HAS_ARG | OPT_PERFILE |
OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_audio_codec },
"force audio codec ('copy' to copy stream)", "codec" },
{ "atag", OPT_AUDIO | HAS_ARG | OPT_EXPERT | OPT_PERFILE |
OPT_OUTPUT, { .func_arg = opt_old2new },
"force audio tag/fourcc", "fourcc/tag" },
{ "vol", OPT_AUDIO | HAS_ARG | OPT_INT, { &audio_volume },
"change audio volume (256=normal)" , "volume" },
{ "sample_fmt", OPT_AUDIO | HAS_ARG | OPT_EXPERT | OPT_SPEC |
OPT_STRING | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(sample_fmts) },
"set sample format", "format" },
{ "channel_layout", OPT_AUDIO | HAS_ARG | OPT_EXPERT | OPT_PERFILE |
OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_channel_layout },
"set channel layout", "layout" },
{ "af", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_filters },
"set audio filters", "filter_graph" },
{ "guess_layout_max", OPT_AUDIO | HAS_ARG | OPT_INT | OPT_SPEC | OPT_EXPERT | OPT_INPUT, { .off = OFFSET(guess_layout_max) },
"set the maximum number of channels to try to guess the channel layout" },
/* subtitle options */
{ "sn", OPT_SUBTITLE | OPT_BOOL | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(subtitle_disable) },
"disable subtitle" },
{ "scodec", OPT_SUBTITLE | HAS_ARG | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_subtitle_codec },
"force subtitle codec ('copy' to copy stream)", "codec" },
{ "stag", OPT_SUBTITLE | HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new }
, "force subtitle tag/fourcc", "fourcc/tag" },
{ "fix_sub_duration", OPT_BOOL | OPT_EXPERT | OPT_SUBTITLE | OPT_SPEC | OPT_INPUT, { .off = OFFSET(fix_sub_duration) },
"fix subtitles duration" },
{ "canvas_size", OPT_SUBTITLE | HAS_ARG | OPT_STRING | OPT_SPEC | OPT_INPUT, { .off = OFFSET(canvas_sizes) },
"set canvas size (WxH or abbreviation)", "size" },
/* grab options */
{ "vc", HAS_ARG | OPT_EXPERT | OPT_VIDEO, { .func_arg = opt_video_channel },
"deprecated, use -channel", "channel" },
{ "tvstd", HAS_ARG | OPT_EXPERT | OPT_VIDEO, { .func_arg = opt_video_standard },
"deprecated, use -standard", "standard" },
{ "isync", OPT_BOOL | OPT_EXPERT, { &input_sync }, "this option is deprecated and does nothing", "" },
/* muxer options */
{ "muxdelay", OPT_FLOAT | HAS_ARG | OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(mux_max_delay) },
"set the maximum demux-decode delay", "seconds" },
{ "muxpreload", OPT_FLOAT | HAS_ARG | OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(mux_preload) },
"set the initial demux-decode delay", "seconds" },
{ "sdp_file", HAS_ARG | OPT_EXPERT | OPT_OUTPUT, { .func_arg = opt_sdp_file },
"specify a file in which to print sdp information", "file" },
{ "time_base", HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(time_bases) },
"set the desired time base hint for output stream (1:24, 1:48000 or 0.04166, 2.0833e-5)", "ratio" },
{ "enc_time_base", HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(enc_time_bases) },
"set the desired time base for the encoder (1:24, 1:48000 or 0.04166, 2.0833e-5). "
"two special values are defined - "
"0 = use frame rate (video) or sample rate (audio),"
"-1 = match source time base", "ratio" },
{ "bsf", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(bitstream_filters) },
"A comma-separated list of bitstream filters", "bitstream_filters" },
{ "absf", HAS_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new },
"deprecated", "audio bitstream_filters" },
{ "vbsf", OPT_VIDEO | HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new },
"deprecated", "video bitstream_filters" },
{ "apre", HAS_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset },
"set the audio options to the indicated preset", "preset" },
{ "vpre", OPT_VIDEO | HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset },
"set the video options to the indicated preset", "preset" },
{ "spre", HAS_ARG | OPT_SUBTITLE | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset },
"set the subtitle options to the indicated preset", "preset" },
{ "fpre", HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset },
"set options from indicated preset file", "filename" },
{ "max_muxing_queue_size", HAS_ARG | OPT_INT | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(max_muxing_queue_size) },
"maximum number of packets that can be buffered while waiting for all streams to initialize", "packets" },
/* data codec support */
{ "dcodec", HAS_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_data_codec },
"force data codec ('copy' to copy stream)", "codec" },
{ "dn", OPT_BOOL | OPT_VIDEO | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(data_disable) },
"disable data" },
#if CONFIG_VAAPI
{ "vaapi_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vaapi_device },
"set VAAPI hardware device (DRM path or X11 display name)", "device" },
#endif
#if CONFIG_QSV
{ "qsv_device", HAS_ARG | OPT_STRING | OPT_EXPERT, { &qsv_device },
"set QSV hardware device (DirectX adapter index, DRM path or X11 display name)", "device"},
#endif
{ "init_hw_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_init_hw_device },
"initialise hardware device", "args" },
{ "filter_hw_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_hw_device },
"set hardware device used when filtering", "device" },
{ NULL, },
};
ffmpeg_options = options;
int i, ret;
BenchmarkTimeStamps ti;
int savedCode = setjmp(ex_buf__);
if (savedCode == 0) {
cleanup();
ffmpeg_var_cleanup();
init_dynload();
@@ -5063,11 +5541,11 @@ int execute(int argc, char **argv)
if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1])
exit_program(69);
exit_program(received_nb_signals ? 255 : main_return_code);
exit_program(received_nb_signals ? 255 : main_ffmpeg_return_code);
} else {
main_return_code = longjmp_value;
main_ffmpeg_return_code = longjmp_value;
}
return main_return_code;
return main_ffmpeg_return_code;
}
+121 -47
View File
@@ -17,6 +17,12 @@
*/
/*
* CHANGES 01.2020
* - ffprobe support changes
*
* CHANGES 12.2019
* - Concurrent execution support
*
* CHANGES 03.2019
* --------------------------------------------------------
* - config.h include removed
@@ -86,7 +92,7 @@ typedef struct HWAccel {
} HWAccel;
typedef struct HWDevice {
char *name;
const char *name;
enum AVHWDeviceType type;
AVBufferRef *device_ref;
} HWDevice;
@@ -578,63 +584,60 @@ typedef struct OutputFile {
int header_written;
} OutputFile;
extern InputStream **input_streams;
extern int nb_input_streams;
extern InputFile **input_files;
extern int nb_input_files;
extern __thread InputStream **input_streams;
extern __thread int nb_input_streams;
extern __thread InputFile **input_files;
extern __thread int nb_input_files;
extern OutputStream **output_streams;
extern int nb_output_streams;
extern OutputFile **output_files;
extern int nb_output_files;
extern __thread OutputStream **output_streams;
extern __thread int nb_output_streams;
extern __thread OutputFile **output_files;
extern __thread int nb_output_files;
extern FilterGraph **filtergraphs;
extern int nb_filtergraphs;
extern __thread FilterGraph **filtergraphs;
extern __thread int nb_filtergraphs;
extern char *vstats_filename;
extern char *sdp_filename;
extern __thread char *vstats_filename;
extern __thread char *sdp_filename;
extern float audio_drift_threshold;
extern float dts_delta_threshold;
extern float dts_error_threshold;
extern __thread float audio_drift_threshold;
extern __thread float dts_delta_threshold;
extern __thread float dts_error_threshold;
extern int audio_volume;
extern int audio_sync_method;
extern int video_sync_method;
extern float frame_drop_threshold;
extern int do_benchmark;
extern int do_benchmark_all;
extern int do_deinterlace;
extern int do_hex_dump;
extern int do_pkt_dump;
extern int copy_ts;
extern int start_at_zero;
extern int copy_tb;
extern int debug_ts;
extern int exit_on_error;
extern int abort_on_flags;
extern int print_stats;
extern int qp_hist;
extern int stdin_interaction;
extern int frame_bits_per_raw_sample;
extern AVIOContext *progress_avio;
extern float max_error_rate;
extern char *videotoolbox_pixfmt;
extern __thread int audio_volume;
extern __thread int audio_sync_method;
extern __thread int video_sync_method;
extern __thread float frame_drop_threshold;
extern __thread int do_benchmark;
extern __thread int do_benchmark_all;
extern __thread int do_deinterlace;
extern __thread int do_hex_dump;
extern __thread int do_pkt_dump;
extern __thread int copy_ts;
extern __thread int start_at_zero;
extern __thread int copy_tb;
extern __thread int debug_ts;
extern __thread int exit_on_error;
extern __thread int abort_on_flags;
extern __thread int print_stats;
extern __thread int qp_hist;
extern __thread int stdin_interaction;
extern __thread int frame_bits_per_raw_sample;
extern __thread AVIOContext *progress_avio;
extern __thread float max_error_rate;
extern __thread char *videotoolbox_pixfmt;
extern int filter_nbthreads;
extern int filter_complex_nbthreads;
extern int vstats_version;
extern __thread int filter_complex_nbthreads;
extern __thread int vstats_version;
extern const AVIOInterruptCB int_cb;
extern __thread const AVIOInterruptCB int_cb;
extern const OptionDef options[];
extern const HWAccel hwaccels[];
extern AVBufferRef *hw_device_ctx;
extern __thread AVBufferRef *hw_device_ctx;
#if CONFIG_QSV
extern char *qsv_device;
extern __thread char *qsv_device;
#endif
extern HWDevice *filter_hw_device;
extern __thread HWDevice *filter_hw_device;
void term_init(void);
void term_exit(void);
@@ -683,4 +686,75 @@ void set_report_callback(void (*callback)(int, float, float, int64_t, int, doubl
void cancel_operation();
int opt_map(void *optctx, const char *opt, const char *arg);
int opt_map_channel(void *optctx, const char *opt, const char *arg);
int opt_recording_timestamp(void *optctx, const char *opt, const char *arg);
int opt_data_frames(void *optctx, const char *opt, const char *arg);
int opt_progress(void *optctx, const char *opt, const char *arg);
int opt_target(void *optctx, const char *opt, const char *arg);
int opt_vsync(void *optctx, const char *opt, const char *arg);
int opt_abort_on(void *optctx, const char *opt, const char *arg);
int opt_qscale(void *optctx, const char *opt, const char *arg);
int opt_profile(void *optctx, const char *opt, const char *arg);
int opt_filter_complex(void *optctx, const char *opt, const char *arg);
int opt_filter_complex_script(void *optctx, const char *opt, const char *arg);
int opt_attach(void *optctx, const char *opt, const char *arg);
int opt_video_frames(void *optctx, const char *opt, const char *arg);
int opt_video_codec(void *optctx, const char *opt, const char *arg);
int opt_sameq(void *optctx, const char *opt, const char *arg);
int opt_timecode(void *optctx, const char *opt, const char *arg);
int opt_vstats_file(void *optctx, const char *opt, const char *arg);
int opt_vstats(void *optctx, const char *opt, const char *arg);
int opt_video_frames(void *optctx, const char *opt, const char *arg);
int opt_old2new(void *optctx, const char *opt, const char *arg);
int opt_streamid(void *optctx, const char *opt, const char *arg);
int opt_bitrate(void *optctx, const char *opt, const char *arg);
int show_hwaccels(void *optctx, const char *opt, const char *arg);
int opt_video_filters(void *optctx, const char *opt, const char *arg);
int opt_audio_frames(void *optctx, const char *opt, const char *arg);
int opt_audio_qscale(void *optctx, const char *opt, const char *arg);
int opt_audio_codec(void *optctx, const char *opt, const char *arg);
int opt_channel_layout(void *optctx, const char *opt, const char *arg);
int opt_preset(void *optctx, const char *opt, const char *arg);
int opt_audio_filters(void *optctx, const char *opt, const char *arg);
int opt_subtitle_codec(void *optctx, const char *opt, const char *arg);
int opt_video_channel(void *optctx, const char *opt, const char *arg);
int opt_video_standard(void *optctx, const char *opt, const char *arg);
int opt_sdp_file(void *optctx, const char *opt, const char *arg);
int opt_data_codec(void *optctx, const char *opt, const char *arg);
int opt_init_hw_device(void *optctx, const char *opt, const char *arg);
int opt_filter_hw_device(void *optctx, const char *opt, const char *arg);
void add_input_streams(OptionsContext *o, AVFormatContext *ic);
void assert_file_overwrite(const char *filename);
void dump_attachment(AVStream *st, const char *filename);
uint8_t *get_line(AVIOContext *s);
void uninit_options(OptionsContext *o);
void init_options(OptionsContext *o);
AVDictionary *strip_specifiers(AVDictionary *dict);
void parse_meta_type(char *arg, char *type, int *index, const char **stream_spec);
int fftools_copy_metadata(char *outspec, char *inspec, AVFormatContext *oc, AVFormatContext *ic, OptionsContext *o);
AVCodec *find_codec_or_die(const char *name, enum AVMediaType type, int encoder);
AVCodec *choose_decoder(OptionsContext *o, AVFormatContext *s, AVStream *st);
int open_input_file(OptionsContext *o, const char *filename);
int get_preset_file_2(const char *preset_name, const char *codec_name, AVIOContext **s);
int choose_encoder(OptionsContext *o, AVFormatContext *s, OutputStream *ost);
OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, enum AVMediaType type, int source_index);
void parse_matrix_coeffs(uint16_t *dest, const char *str);
uint8_t *fftools_read_file(const char *filename);
char *get_ost_filters(OptionsContext *o, AVFormatContext *oc, OutputStream *ost);
void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc, const OutputStream *ost, enum AVMediaType type);
OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_data_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_unknown_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_attachment_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc, int source_index);
int copy_chapters(InputFile *ifile, OutputFile *ofile, int copy_metadata);
void init_output_filter(OutputFilter *ofilter, OptionsContext *o, AVFormatContext *oc);
int init_complex_filters(void);
int open_output_file(OptionsContext *o, const char *filename);
int opt_default_new(OptionsContext *o, const char *opt, const char *arg);
int open_files(OptionGroupList *l, const char *inout, int (*open_file)(OptionsContext*, const char*));
#endif /* FFTOOLS_FFMPEG_H */
+10 -4
View File
@@ -17,6 +17,9 @@
*/
/*
* CHANGES 12.2019
* - Concurrent execution support
*
* CHANGES 08.2018
* --------------------------------------------------------
* - fftools_ prefix added to file name and parent header
@@ -28,8 +31,8 @@
#include "fftools_ffmpeg.h"
static int nb_hw_devices;
static HWDevice **hw_devices;
__thread int nb_hw_devices;
__thread HWDevice **hw_devices;
static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type)
{
@@ -105,7 +108,7 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
// -> av_hwdevice_ctx_create_derived()
AVDictionary *options = NULL;
char *type_name = NULL, *name = NULL, *device = NULL;
const char *type_name = NULL, *name = NULL, *device = NULL;
enum AVHWDeviceType type;
HWDevice *dev, *src;
AVBufferRef *device_ref = NULL;
@@ -161,11 +164,13 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
++p;
q = strchr(p, ',');
if (q) {
if (q - p > 0) {
device = av_strndup(p, q - p);
if (!device) {
err = AVERROR(ENOMEM);
goto fail;
}
}
err = av_dict_parse_string(&options, q + 1, "=", ",", 0);
if (err < 0) {
errmsg = "failed to parse options";
@@ -174,7 +179,8 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
}
err = av_hwdevice_ctx_create(&device_ref, type,
device ? device : p, options, 0);
q ? device : p[0] ? p : NULL,
options, 0);
if (err < 0)
goto fail;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+17 -10
View File
@@ -21,6 +21,7 @@
* CHANGES 08.2019
* --------------------------------------------------------
* - lastCommandOutput methods introduced
* - AV_LOG_STDERR introduced
*
* CHANGES 04.2019
* --------------------------------------------------------
@@ -53,6 +54,7 @@
#include "libavutil/bprint.h"
#include "fftools_ffmpeg.h"
#include "mobileffmpeg.h"
#include "mobileffprobe.h"
/** Callback data structure */
struct CallbackData {
@@ -119,21 +121,24 @@ JNINativeMethod configMethods[] = {
{"getNativeLogLevel", "()I", (void*) Java_com_arthenica_mobileffmpeg_Config_getNativeLogLevel},
{"getNativeFFmpegVersion", "()Ljava/lang/String;", (void*) Java_com_arthenica_mobileffmpeg_Config_getNativeFFmpegVersion},
{"getNativeVersion", "()Ljava/lang/String;", (void*) Java_com_arthenica_mobileffmpeg_Config_getNativeVersion},
{"nativeExecute", "([Ljava/lang/String;)I", (void*) Java_com_arthenica_mobileffmpeg_Config_nativeExecute},
{"nativeCancel", "()V", (void*) Java_com_arthenica_mobileffmpeg_Config_nativeCancel},
{"nativeFFmpegExecute", "([Ljava/lang/String;)I", (void*) Java_com_arthenica_mobileffmpeg_Config_nativeFFmpegExecute},
{"nativeFFmpegCancel", "()V", (void*) Java_com_arthenica_mobileffmpeg_Config_nativeFFmpegCancel},
{"nativeFFprobeExecute", "([Ljava/lang/String;)I", (void*) Java_com_arthenica_mobileffmpeg_Config_nativeFFprobeExecute},
{"registerNewNativeFFmpegPipe", "(Ljava/lang/String;)I", (void*) Java_com_arthenica_mobileffmpeg_Config_registerNewNativeFFmpegPipe},
{"getNativeBuildDate", "()Ljava/lang/String;", (void*) Java_com_arthenica_mobileffmpeg_Config_getNativeBuildDate},
{"setNativeEnvironmentVariable", "(Ljava/lang/String;Ljava/lang/String;)I", (void*) Java_com_arthenica_mobileffmpeg_Config_setNativeEnvironmentVariable}
};
/** Forward declaration for function defined in fftools_ffmpeg.c */
int execute(int argc, char **argv);
int ffmpeg_execute(int argc, char **argv);
/** DEFINES LINE SIZE USED FOR LOGGING */
#define LOG_LINE_SIZE 1024
static const char *avutil_log_get_level_str(int level) {
switch (level) {
case AV_LOG_STDERR:
return "stderr";
case AV_LOG_QUIET:
return "quiet";
case AV_LOG_DEBUG:
@@ -462,7 +467,9 @@ void mobileffmpeg_log_callback_function(void *ptr, int level, const char* format
level &= 0xff;
}
int activeLogLevel = av_log_get_level();
if ((activeLogLevel == AV_LOG_QUIET) || (level > activeLogLevel)) {
// AV_LOG_STDERR logs are always redirected
if ((activeLogLevel == AV_LOG_QUIET && level != AV_LOG_STDERR) || (level > activeLogLevel)) {
return;
}
@@ -584,7 +591,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, localConfigClass, configMethods, 11) < 0) {
if ((*env)->RegisterNatives(env, localConfigClass, configMethods, 12) < 0) {
LOGE("OnLoad failed to RegisterNatives for class %s.\n", configClassName);
return JNI_FALSE;
}
@@ -730,14 +737,14 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_mobileffmpeg_Config_getNativeVersio
}
/**
* Synchronously executes FFmpeg command natively with arguments provided.
* Synchronously executes FFmpeg natively with arguments provided.
*
* @param env pointer to native method interface
* @param object reference to the class on which this method is invoked
* @param stringArray reference to the object holding FFmpeg command arguments
* @return zero on successful execution, non-zero on error
*/
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeExecute(JNIEnv *env, jclass object, jobjectArray stringArray) {
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeFFmpegExecute(JNIEnv *env, jclass object, jobjectArray stringArray) {
jstring *tempArray = NULL;
int argumentCount = 1;
char **argv = NULL;
@@ -771,7 +778,7 @@ JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeExecute(JNIE
clearLastCommandOutput();
// RUN
int retCode = execute(argumentCount, argv);
int retCode = ffmpeg_execute(argumentCount, argv);
// CLEANUP
if (tempArray != NULL) {
@@ -788,12 +795,12 @@ JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeExecute(JNIE
}
/**
* Cancels an ongoing operation natively.
* Cancels an ongoing FFmpeg operation natively.
*
* @param env pointer to native method interface
* @param object reference to the class on which this method is invoked
*/
JNIEXPORT void JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeCancel(JNIEnv *env, jclass object) {
JNIEXPORT void JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeFFmpegCancel(JNIEnv *env, jclass object) {
cancel_operation();
}
+5 -5
View File
@@ -27,7 +27,7 @@
#include "libavutil/ffversion.h"
/** Library version string */
#define MOBILE_FFMPEG_VERSION "4.3"
#define MOBILE_FFMPEG_VERSION "4.3.1"
/** Defines tag used for Android logging. */
#define LIB_NAME "mobile-ffmpeg"
@@ -91,17 +91,17 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_mobileffmpeg_Config_getNativeVersio
/*
* Class: com_arthenica_mobileffmpeg_Config
* Method: nativeExecute
* Method: nativeFFmpegExecute
* Signature: ([Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeExecute(JNIEnv *, jclass, jobjectArray);
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeFFmpegExecute(JNIEnv *, jclass, jobjectArray);
/*
* Class: com_arthenica_mobileffmpeg_Config
* Method: nativeCancel
* Method: nativeFFmpegCancel
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeCancel(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeFFmpegCancel(JNIEnv *, jclass);
/*
* Class: com_arthenica_mobileffmpeg_Config
@@ -17,29 +17,8 @@
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg;
#include <stdio.h>
#include <setjmp.h>
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.arthenica.mobileffmpeg", appContext.getPackageName());
}
}
/** Holds information to implement exception handling. */
__thread jmp_buf ex_buf__;
@@ -24,6 +24,6 @@
#include <setjmp.h>
/** Holds information to implement exception handling. */
jmp_buf ex_buf__;
extern __thread jmp_buf ex_buf__;
#endif // MOBILE_FFMPEG_EXCEPTION_H
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
#include <jni.h>
#include "libavcodec/jni.h"
/** Forward declaration for function defined in fftools_ffplay.c */
int ffplay_execute(int argc, char **argv);
/** Forward declaration for functions defined in SDl_android.c */
void set_mobile_ffmpeg_ffplay_execute(int (*ffplay_execute_function)(int argc, char **argv));
jint SDL_Android_Initialize(JavaVM* vm, void* reserved);
/**
* Initializes SDL for FFplay. It must be called before other SDL functions.
*/
JNIEXPORT void JNICALL Java_com_arthenica_mobileffmpeg_FFplay_nativeSDLInit(JNIEnv *env, jclass object) {
set_mobile_ffmpeg_ffplay_execute(ffplay_execute);
JavaVM *globalVm = av_jni_get_java_vm(NULL);
if (globalVm) {
SDL_Android_Initialize(globalVm, NULL);
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MOBILE_FFPLAY_H
#define MOBILE_FFPLAY_H
#include <jni.h>
/*
* Class: com_arthenica_mobileffmpeg_Config
* Method: nativeFFplayExecute
* Signature: ([Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeFFplayExecute(JNIEnv *, jclass, jobjectArray);
#endif /* MOBILE_FFPLAY_H */
+91
View File
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "config.h"
#include "libavcodec/jni.h"
#include "libavutil/bprint.h"
#include "mobileffmpeg.h"
/** Forward declaration for function defined in fftools_ffprobe.c */
int ffprobe_execute(int argc, char **argv);
/** Forward declaration for function defined in mobileffmpeg.c */
void clearLastCommandOutput();
/**
* Synchronously executes FFprobe natively with arguments provided.
*
* @param env pointer to native method interface
* @param object reference to the class on which this method is invoked
* @param stringArray reference to the object holding FFprobe command arguments
* @return zero on successful execution, non-zero on error
*/
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeFFprobeExecute(JNIEnv *env, jclass object, jobjectArray stringArray) {
jstring *tempArray = NULL;
int argumentCount = 1;
char **argv = NULL;
if (stringArray != NULL) {
int programArgumentCount = (*env)->GetArrayLength(env, stringArray);
argumentCount = programArgumentCount + 1;
tempArray = (jstring *) av_malloc(sizeof(jstring) * programArgumentCount);
}
/* PRESERVE USAGE FORMAT
*
* ffprobe <arguments>
*/
argv = (char **)av_malloc(sizeof(char*) * (argumentCount));
argv[0] = (char *)av_malloc(sizeof(char) * (strlen(LIB_NAME) + 1));
strcpy(argv[0], LIB_NAME);
// PREPARE
if (stringArray != NULL) {
for (int i = 0; i < (argumentCount - 1); i++) {
tempArray[i] = (jstring) (*env)->GetObjectArrayElement(env, stringArray, i);
if (tempArray[i] != NULL) {
argv[i + 1] = (char *) (*env)->GetStringUTFChars(env, tempArray[i], 0);
}
}
}
// LAST COMMAND OUTPUT SHOULD BE CLEARED BEFORE STARTING A NEW EXECUTION
clearLastCommandOutput();
// RUN
int retCode = ffprobe_execute(argumentCount, argv);
// CLEANUP
if (tempArray != NULL) {
for (int i = 0; i < (argumentCount - 1); i++) {
(*env)->ReleaseStringUTFChars(env, tempArray[i], argv[i + 1]);
}
av_free(tempArray);
}
av_free(argv[0]);
av_free(argv);
return retCode;
}
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MOBILE_FFPROBE_H
#define MOBILE_FFPROBE_H
#include <jni.h>
/*
* Class: com_arthenica_mobileffmpeg_Config
* Method: nativeFFprobeExecute
* Signature: ([Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeFFprobeExecute(JNIEnv *, jclass, jobjectArray);
#endif /* MOBILE_FFPROBE_H */
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018 Taner Sener
* Copyright (c) 2018-2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
@@ -31,35 +31,36 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import static com.arthenica.mobileffmpeg.FFmpeg.getBuildDate;
import static com.arthenica.mobileffmpeg.FFmpeg.getVersion;
/**
* <p>This class is used to configure MobileFFmpeg library utilities/tools.
*
* <p>1. {@link LogCallback}: By default this class redirects FFmpeg output to Logcat. As another
* option, it is possible not to print messages to Logcat and pass them to a {@link LogCallback}
* function. This function can decide whether to print these logs, show them inside another
* container or ignore them.
* <p>1. {@link LogCallback}: This class redirects FFmpeg/FFprobe output to Logcat by default. As
* an alternative, it is possible not to print messages to Logcat and pass them to a
* {@link LogCallback} function. This function can decide whether to print these logs, show them
* inside another container or ignore them.
*
* <p>2. {@link #setLogLevel(Level)}/{@link #getLogLevel()}: Use this methods to see/control FFmpeg
* log severity.
* <p>2. {@link #setLogLevel(Level)}/{@link #getLogLevel()}: Use this methods to set/get
* FFmpeg/FFprobe log severity.
*
* <p>3. {@link StatisticsCallback}: It is possible to receive statistics about ongoing operation by
* defining a {@link StatisticsCallback} function or by calling {@link #getLastReceivedStatistics()}
* method.
* <p>3. {@link StatisticsCallback}: It is possible to receive statistics about an ongoing
* operation by defining a {@link StatisticsCallback} function or by calling
* {@link #getLastReceivedStatistics()} method.
*
* <p>4. Font configuration: It is possible to register custom fonts with
* {@link #setFontconfigConfigurationPath(String)} and
* {@link #setFontDirectory(Context, String, Map)} methods.
*
* <p>PS: This class is introduced in v2.1 as an enhanced version of older <code>Log</code> class.
*
* @author Taner Sener
* @since v2.1
*/
public class Config {
public static final int RETURN_CODE_SUCCESS = 0;
public static final int RETURN_CODE_CANCEL = 255;
private static int lastReturnCode = 0;
/**
* Defines tag used for logging.
*/
@@ -75,10 +76,6 @@ public class Config {
private static Statistics lastReceivedStatistics;
private static final AtomicReference<StringBuffer> systemCommandOutputReference;
private static boolean runningSystemCommand;
private static int lastCreatedPipeIndex;
static {
@@ -105,6 +102,8 @@ public class Config {
/* ALL MOBILE-FFMPEG LIBRARIES LOADED AT STARTUP */
Abi.class.getName();
FFmpeg.class.getName();
FFprobe.class.getName();
FFplay.class.getName();
/*
* NEON supported arm-v7a library has a different name
@@ -140,12 +139,8 @@ public class Config {
lastReceivedStatistics = new Statistics();
Config.enableRedirection();
enableRedirection();
systemCommandOutputReference = new AtomicReference<>();
systemCommandOutputReference.set(new StringBuffer());
runningSystemCommand = false;
lastCreatedPipeIndex = 0;
}
@@ -157,14 +152,15 @@ public class Config {
/**
* <p>Enables log and statistics redirection.
* <p>When redirection is not enabled FFmpeg logs are printed to stderr. By enabling redirection, they are routed
* to Logcat and can be routed further to a callback function.
* <p>Statistics redirection behaviour is similar. Statistics are not printed at all if redirection is not enabled.
* If it is enabled then it is possible to define a statistics callback function but if you don't, they are not
* printed anywhere and only saved as <code>lastReceivedStatistics</code> data which can be polled with
* <p>When redirection is not enabled FFmpeg/FFprobe logs are printed to stderr. By enabling
* redirection, they are routed to Logcat and can be routed further to a callback function.
* <p>Statistics redirection behaviour is similar. Statistics are not printed at all if
* redirection is not enabled. If it is enabled then it is possible to define a statistics
* callback function but if you don't, they are not printed anywhere and only saved as
* <code>lastReceivedStatistics</code> data which can be polled with
* {@link #getLastReceivedStatistics()}.
* <p>Note that redirection is enabled by default. If you do not want to use its functionality please use
* {@link #disableRedirection()} to disable it.
* <p>Note that redirection is enabled by default. If you do not want to use its functionality
* please use {@link #disableRedirection()} to disable it.
*/
public static void enableRedirection() {
enableNativeRedirection();
@@ -199,7 +195,7 @@ public class Config {
}
/**
* <p>Sets a callback function to redirect FFmpeg logs.
* <p>Sets a callback function to redirect FFmpeg/FFprobe logs.
*
* @param newLogCallback new log callback function or NULL to disable a previously defined callback
*/
@@ -226,16 +222,8 @@ public class Config {
final Level level = Level.from(levelValue);
final String text = new String(logMessage);
if (runningSystemCommand) {
// REDIRECT SYSTEM OUTPUT
if (activeLogLevel != Level.AV_LOG_QUIET && levelValue <= activeLogLevel.getValue()) {
systemCommandOutputReference.get().append(text);
}
return;
}
if (activeLogLevel == Level.AV_LOG_QUIET || levelValue > activeLogLevel.getValue()) {
// AV_LOG_STDERR logs are always redirected
if ((activeLogLevel == Level.AV_LOG_QUIET && levelValue != Level.AV_LOG_STDERR.getValue()) || levelValue > activeLogLevel.getValue()) {
// LOG NEITHER PRINTED NOR FORWARDED
return;
}
@@ -257,6 +245,7 @@ public class Config {
android.util.Log.d(TAG, text);
}
break;
case AV_LOG_STDERR:
case AV_LOG_VERBOSE: {
android.util.Log.v(TAG, text);
}
@@ -332,7 +321,7 @@ public class Config {
* @return zero on success, non-zero on error
*/
public static int setFontconfigConfigurationPath(final String path) {
return Config.setNativeEnvironmentVariable("FONTCONFIG_PATH", path);
return setNativeEnvironmentVariable("FONTCONFIG_PATH", path);
}
/**
@@ -478,69 +467,6 @@ public class Config {
}
}
/**
* Executes system command. System command is not logged to output.
*
* @param arguments command arguments
* @param commandOutputEndPatternList list of patterns which will indicate that operation has ended
* @param successPattern success pattern
* @param timeout execution timeout
* @return return code
*/
static int systemExecute(final String[] arguments, final List<String> commandOutputEndPatternList, final String successPattern, final long timeout) {
if (successPattern != null) {
commandOutputEndPatternList.add(successPattern);
}
systemCommandOutputReference.set(new StringBuffer());
runningSystemCommand = true;
int rc = Config.nativeExecute(arguments);
long totalWaitTime = 0;
try {
while (!systemCommandOutputContainsPattern(commandOutputEndPatternList) && (totalWaitTime < timeout)) {
synchronized (systemCommandOutputReference) {
systemCommandOutputReference.wait(20);
}
totalWaitTime += 20;
}
} catch (final InterruptedException e) {
Log.w(TAG, "systemExecute operation interrupted.", e);
}
runningSystemCommand = false;
nativeCancel();
StringBuffer stringBuffer = systemCommandOutputReference.get();
if ((successPattern != null) && (stringBuffer != null) && stringBuffer.toString().contains(successPattern)) {
return 0;
} else {
return rc;
}
}
private static boolean systemCommandOutputContainsPattern(final List<String> patternList) {
String string = systemCommandOutputReference.get().toString();
for (String pattern : patternList) {
if (string.contains(pattern)) {
return true;
}
}
return false;
}
/**
* Returns output of last executed system command.
*
* @return output of last executed system command
*/
static String getSystemCommandOutput() {
return systemCommandOutputReference.get().toString();
}
/**
* Returns the list of camera ids supported.
*
@@ -557,29 +483,142 @@ public class Config {
return detectedCameraIdList;
}
/**
* <p>Returns FFmpeg version bundled within the library.
*
* @return FFmpeg version
*/
public static String getFFmpegVersion() {
return getNativeFFmpegVersion();
}
/**
* <p>Returns MobileFFmpeg library version.
*
* @return MobileFFmpeg version
*/
public static String getVersion() {
if (AbiDetect.isNativeLTSBuild()) {
return String.format("%s-lts", getNativeVersion());
} else {
return getNativeVersion();
}
}
/**
* <p>Returns whether MobileFFmpeg release is a long term release or not.
*
* @return YES or NO
*/
public static boolean isLTSBuild() {
return AbiDetect.isNativeLTSBuild();
}
/**
* <p>Returns MobileFFmpeg library build date.
*
* @return MobileFFmpeg library build date
*/
public static String getBuildDate() {
return getNativeBuildDate();
}
/**
* <p>Returns return code of last executed command.
*
* @return return code of last executed command
* @since 3.0
*/
public static int getLastReturnCode() {
return lastReturnCode;
}
/**
* <p>Returns log output of last executed single FFmpeg/FFprobe command.
*
* <p>This method does not support executing multiple concurrent commands. If you execute
* multiple commands at the same time, this method will return output from all executions.
*
* <p>Please note that disabling redirection using {@link Config#disableRedirection()} method
* also disables this functionality.
*
* @return output of the last executed command
* @since 3.0
*/
public static String getLastCommandOutput() {
String nativeLastCommandOutput = getNativeLastCommandOutput();
if (nativeLastCommandOutput != null) {
// REPLACING CH(13) WITH CH(10)
nativeLastCommandOutput = nativeLastCommandOutput.replace('\r', '\n');
}
return nativeLastCommandOutput;
}
/**
* <p>Prints the output of the last executed FFmpeg/FFprobe command to the Logcat at the
* specified priority.
*
* <p>This method does not support executing multiple concurrent commands. If you execute
* multiple commands at the same time, this method will print output from all executions.
*
* @param logPriority one of {@link Log#VERBOSE}, {@link Log#DEBUG}, {@link Log#INFO},
* {@link Log#WARN}, {@link Log#ERROR}, {@link Log#ASSERT}
* @since 4.3
*/
public static void printLastCommandOutput(int logPriority) {
final int LOGGER_ENTRY_MAX_LEN = 4 * 1000;
String buffer = getLastCommandOutput();
do {
if (buffer.length() <= LOGGER_ENTRY_MAX_LEN) {
Log.println(logPriority, Config.TAG, buffer);
buffer = "";
} else {
final int index = buffer.substring(0, LOGGER_ENTRY_MAX_LEN).lastIndexOf('\n');
if (index < 0) {
Log.println(logPriority, Config.TAG, buffer.substring(0, LOGGER_ENTRY_MAX_LEN));
buffer = buffer.substring(LOGGER_ENTRY_MAX_LEN);
} else {
Log.println(logPriority, Config.TAG, buffer.substring(0, index));
buffer = buffer.substring(index);
}
}
} while (buffer.length() > 0);
}
/**
* Updates return code value for the last executed command.
*
* @param newLastReturnCode new last return code value
*/
static void setLastReturnCode(int newLastReturnCode) {
lastReturnCode = newLastReturnCode;
}
/**
* <p>Enables native redirection. Necessary for log and statistics callback functions.
*/
private static native void enableNativeRedirection();
private native static void enableNativeRedirection();
/**
* <p>Disables native redirection
*/
private static native void disableNativeRedirection();
private native static void disableNativeRedirection();
/**
* Sets native log level
*
* @param level log level
*/
private static native void setNativeLogLevel(int level);
private native static void setNativeLogLevel(int level);
/**
* Returns native log level.
*
* @return log level
*/
private static native int getNativeLogLevel();
private native static int getNativeLogLevel();
/**
* <p>Returns FFmpeg version bundled within the library natively.
@@ -601,13 +640,21 @@ public class Config {
* @param arguments FFmpeg command options/arguments as string array
* @return zero on successful execution, 255 on user cancel and non-zero on error
*/
native static int nativeExecute(final String[] arguments);
native static int nativeFFmpegExecute(final String[] arguments);
/**
* <p>Cancels an ongoing operation natively. This function does not wait for termination to
* complete and returns immediately.
* <p>Cancels an ongoing FFmpeg operation natively. This function does not wait for termination
* to complete and returns immediately.
*/
native static void nativeCancel();
native static void nativeFFmpegCancel();
/**
* <p>Synchronously executes FFprobe natively with arguments provided.
*
* @param arguments FFprobe command options/arguments as string array
* @return zero on successful execution, 255 on user cancel and non-zero on error
*/
native static int nativeFFprobeExecute(final String[] arguments);
/**
* <p>Creates natively a new named pipe to use in <code>FFmpeg</code> operations.
@@ -633,12 +680,12 @@ public class Config {
* @param variableValue environment variable value
* @return zero on success, non-zero on error
*/
native static int setNativeEnvironmentVariable(final String variableName, final String variableValue);
public native static int setNativeEnvironmentVariable(final String variableName, final String variableValue);
/**
* <p>Returns log output of the last executed command natively.
* <p>Returns log output of the last executed single command natively.
*
* @return output of the last executed command
* @return output of the last executed single command
*/
native static String getNativeLastCommandOutput();
@@ -19,18 +19,14 @@
package com.arthenica.mobileffmpeg;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* <p>Main class for FFmpeg operations. Provides {@link #execute(String...)} method to execute
* FFmpeg commands.
* <pre>
* int rc = FFmpeg.execute("-i", "file1.mp4", "-c:v", "libxvid", "file1.avi");
* int rc = FFmpeg.execute("-i file1.mp4 -c:v libxvid file1.avi");
* Log.i(Config.TAG, String.format("Command execution %s.", (rc == 0?"completed successfully":"failed with rc=" + rc));
* </pre>
*
@@ -39,16 +35,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public class FFmpeg {
public static final int RETURN_CODE_SUCCESS = 0;
public static final int RETURN_CODE_CANCEL = 255;
public static final int RETURN_CODE_MULTIPLE_EXECUTIONS_NOT_ALLOWED = 300;
private static int lastReturnCode = 0;
private static final AtomicBoolean started = new AtomicBoolean(false);
static {
AbiDetect.class.getName();
Config.class.getName();
@@ -60,28 +46,6 @@ public class FFmpeg {
private FFmpeg() {
}
/**
* <p>Returns FFmpeg version bundled within the library.
*
* @return FFmpeg version
*/
public static String getFFmpegVersion() {
return Config.getNativeFFmpegVersion();
}
/**
* <p>Returns MobileFFmpeg library version.
*
* @return MobileFFmpeg version
*/
public static String getVersion() {
if (AbiDetect.isNativeLTSBuild()) {
return String.format("%s-lts", Config.getNativeVersion());
} else {
return Config.getNativeVersion();
}
}
/**
* <p>Synchronously executes FFmpeg with arguments provided.
*
@@ -89,13 +53,9 @@ public class FFmpeg {
* @return zero on successful execution, 255 on user cancel and non-zero on error
*/
public static int execute(final String[] arguments) {
if (started.compareAndSet(false, true)) {
lastReturnCode = Config.nativeExecute(arguments);
started.compareAndSet(true, false);
} else {
Log.e(Config.TAG, "execute cancelled. Multiple executions not supported.");
lastReturnCode = RETURN_CODE_MULTIPLE_EXECUTIONS_NOT_ALLOWED;
}
final int lastReturnCode = Config.nativeFFmpegExecute(arguments);
Config.setLastReturnCode(lastReturnCode);
return lastReturnCode;
}
@@ -118,7 +78,8 @@ public class FFmpeg {
/**
* <p>Synchronously executes FFmpeg command provided. Space character is used to split command
* into arguments.
* into arguments. You can use single and double quote characters to specify arguments inside
* your command.
*
* @param command FFmpeg command
* @return zero on successful execution, 255 on user cancel and non-zero on error
@@ -132,89 +93,7 @@ public class FFmpeg {
* returns immediately.
*/
public static void cancel() {
Config.nativeCancel();
}
/**
* <p>Returns return code of last executed command.
*
* @return return code of last executed command
* @since 3.0
*/
public static int getLastReturnCode() {
return lastReturnCode;
}
/**
* <p>Returns log output of the last executed command. Please note that disabling redirection
* using {@link Config#disableRedirection()} method also disables this functionality.
*
* @return output of the last executed command
* @since 3.0
*/
public static String getLastCommandOutput() {
String nativeLastCommandOutput = Config.getNativeLastCommandOutput();
if (nativeLastCommandOutput != null) {
// REPLACING CH(13) WITH CH(10)
nativeLastCommandOutput = nativeLastCommandOutput.replace('\r', '\n');
}
return nativeLastCommandOutput;
}
/**
* <p>Returns media information for given file.
*
* @param path path or uri of media file
* @return media information
* @since 3.0
*/
public static MediaInformation getMediaInformation(final String path) {
return getMediaInformation(path, 10000L);
}
/**
* <p>Returns media information for given file.
*
* @param path path or uri of media file
* @param timeout complete timeout
* @return media information
* @since 3.0
*/
public static MediaInformation getMediaInformation(final String path, final Long timeout) {
final int rc;
if (started.compareAndSet(false, true)) {
rc = Config.systemExecute(new String[]{"-v", "info", "-hide_banner", "-i", path}, new ArrayList<>(Arrays.asList("Press [q] to stop, [?] for help", "No such file or directory", "Input/output error", "Conversion failed", "HTTP error")), "At least one output file must be specified", timeout);
started.compareAndSet(true, false);
} else {
Log.e(Config.TAG, "getMediaInformation cancelled. Multiple executions not supported.");
rc = RETURN_CODE_MULTIPLE_EXECUTIONS_NOT_ALLOWED;
}
if (rc == 0) {
return MediaInformationParser.from(Config.getSystemCommandOutput());
} else {
Log.i(Config.TAG, Config.getSystemCommandOutput());
return null;
}
}
/**
* <p>Returns whether MobileFFmpeg release is a long term release or not.
*
* @return YES or NO
*/
public static boolean isLTSBuild() {
return AbiDetect.isNativeLTSBuild();
}
/**
* <p>Returns MobileFFmpeg library build date.
*
* @return MobileFFmpeg library build date
*/
public static String getBuildDate() {
return Config.getNativeBuildDate();
Config.nativeFFmpegCancel();
}
/**
@@ -274,32 +153,4 @@ public class FFmpeg {
return argumentList.toArray(new String[0]);
}
/**
* <p>Prints the output of the last executed command to the logcat at the specified priority.
*
* @param logPriority one of {@link Log#VERBOSE}, {@link Log#DEBUG}, {@link Log#INFO},
* {@link Log#WARN}, {@link Log#ERROR}, {@link Log#ASSERT}
* @since 4.3
*/
public static void printLastCommandOutput(int logPriority) {
final int LOGGER_ENTRY_MAX_LEN = 4 * 1000;
String buffer = FFmpeg.getLastCommandOutput();
do {
if (buffer.length() <= LOGGER_ENTRY_MAX_LEN) {
Log.println(logPriority, Config.TAG, buffer);
buffer = "";
} else {
final int index = buffer.substring(0, LOGGER_ENTRY_MAX_LEN).lastIndexOf('\n');
if (index < 0) {
Log.println(logPriority, Config.TAG, buffer.substring(0, LOGGER_ENTRY_MAX_LEN));
buffer = buffer.substring(LOGGER_ENTRY_MAX_LEN);
} else {
Log.println(logPriority, Config.TAG, buffer.substring(0, index));
buffer = buffer.substring(index);
}
}
} while (buffer.length() > 0);
}
}
@@ -0,0 +1,526 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg;
import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import com.arthenica.mobileffmpeg.player.AudioHandler;
import com.arthenica.mobileffmpeg.player.ControllerHandler;
import com.arthenica.mobileffmpeg.player.PlayerManager;
import java.io.IOException;
import java.io.InputStream;
import static com.arthenica.mobileffmpeg.Config.TAG;
public class FFplay {
private static final boolean enabled;
private static boolean separateMouseAndTouch;
private static AudioHandler audioHandler;
private static ControllerHandler controllerHandler;
private static PlayerManager playerManager;
static {
/* MOUSE AND TOUCH IS THE SAME DEVICE BY DEFAULT */
separateMouseAndTouch = false;
/* FFPLAY DEPENDS ON SDL. CHECK WHETHER IT IS ENABLED OR NOT */
if (Config.getExternalLibraries().contains("sdl2")) {
init();
/* FFPLAY METHODS ARE ENABLED ONLY IF INITIALIZATION COMPLETES SUCCESSFULLY */
enabled = true;
} else {
enabled = false;
}
}
static void init() {
// ENABLE SDL FIRST
nativeSDLInit();
// COMPLETE OTHER COMPONENTS AFTER
nativePlayerInit();
nativeAudioInit();
nativeControllerInit();
}
/**
* <p>Synchronously executes FFplay with arguments provided.
*
* @param arguments FFplay command options/arguments as string array
* @return zero on successful execution, 255 on user cancel and non-zero on error
*/
public static int execute(final String[] arguments) {
if (enabled) {
return nativePlayerRun(arguments);
} else {
throw new RuntimeException("sdl not found. FFplay requires sdl to run.");
}
}
public static boolean isSeparateMouseAndTouch() {
return separateMouseAndTouch;
}
public static void setSeparateMouseAndTouch(boolean separateMouseAndTouch) {
FFplay.separateMouseAndTouch = separateMouseAndTouch;
}
public static AudioHandler getAudioHandler() {
return audioHandler;
}
public static ControllerHandler getControllerHandler() {
return controllerHandler;
}
public static PlayerManager getPlayerManager() {
return playerManager;
}
public static void setAudioHandler(final AudioHandler newAudioHandler) {
audioHandler = newAudioHandler;
}
public static void setControllerManager(final ControllerHandler newControllerHandler) {
controllerHandler = newControllerHandler;
}
public static void setPlayerManager(PlayerManager newPlayerManager) {
playerManager = newPlayerManager;
}
public static String playerNativeGetHint(final String name) {
return nativePlayerNativeGetHint(name);
}
public static void inputSetComposingText(final String text, final int newCursorPosition) {
nativeInputSetComposingText(text, newCursorPosition);
}
public static void inputGenerateScancodeForUnichar(final char c) {
nativeInputGenerateScancodeForUnichar(c);
}
public static void inputCommitText(String text, int newCursorPosition) {
nativeInputCommitText(text, newCursorPosition);
}
public static void playerNativeSetenv(final String name, final String value) {
//@TODO We already have this method under Config
nativePlayerNativeSetenv(name, value);
}
public static void playerOnDropFile(final String filename) {
nativePlayerOnDropFile(filename);
}
public static void playerNativeLowMemory() {
nativePlayerNativeLowMemory();
}
public static void playerNativeQuit() {
nativePlayerNativeQuit();
}
public static void playerNativePause() {
nativePlayerNativePause();
}
public static void playerNativeResume() {
nativePlayerNativeResume();
}
public static void playerOnKeyDown(final int keyCode) {
nativePlayerOnKeyDown(keyCode);
}
public static void playerOnKeyUp(final int keyCode) {
nativePlayerOnKeyUp(keyCode);
}
public static void playerOnKeyboardFocusLost() {
nativePlayerOnKeyboardFocusLost();
}
public static void playerOnClipboardChanged() {
nativePlayerOnClipboardChanged();
}
public static void playerOnSurfaceChanged() {
nativePlayerOnSurfaceChanged();
}
public static void playerOnSurfaceDestroyed() {
nativePlayerOnSurfaceDestroyed();
}
public static void playerOnMouse(final int button, final int action, final float x, final float y) {
nativePlayerOnMouse(button, action, x, y);
}
public static int controllerAddHaptic(final int deviceId, final String name) {
return nativeControllerAddHaptic(deviceId, name);
}
public static int controllerRemoveHaptic(final int deviceId) {
return nativeControllerRemoveHaptic(deviceId);
}
public static int controllerAddJoystick(final int deviceId, final String name, final String desc, final int isAccelerometer, final int nButtons, final int nAxes, final int nHats, final int nBalls) {
return nativeControllerAddJoystick(deviceId, name, desc, isAccelerometer, nButtons, nAxes, nHats, nBalls);
}
public static int controllerRemoveJoystick(final int deviceId) {
return nativeControllerRemoveJoystick(deviceId);
}
public static void controllerOnJoy(final int deviceId, final int axis, final float value) {
nativeControllerOnJoy(deviceId, axis, value);
}
public static void controllerOnHat(final int deviceId, final int hatId, final int x, final int y) {
nativeControllerOnHat(deviceId, hatId, x, y);
}
public static void playerOnResize(final int x, final int y, final int format, final float rate) {
nativePlayerOnResize(x, y, format, rate);
}
public static int controllerOnPadDown(final int deviceId, final int keycode) {
return nativeControllerOnPadDown(deviceId, keycode);
}
public static int controllerOnPadUp(final int deviceId, final int keycode) {
return nativeControllerOnPadUp(deviceId, keycode);
}
public static void playerOnTouch(final int touchDevId, final int pointerFingerId, final int action, final float x, final float y, final float p) {
nativePlayerOnTouch(touchDevId, pointerFingerId, action, x, y, p);
}
public static void playerOnAccel(final float x, final float y, final float z) {
nativePlayerOnAccel(x, y, z);
}
/* AUDIO FUNCTIONS CALLED BY NATIVE THREADS */
static int audioOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, final int desiredFrames) {
if (audioHandler != null) {
return audioHandler.audioOpen(sampleRate, is16Bit, isStereo, desiredFrames);
} else {
return -1;
}
}
static void audioWriteShortBuffer(final short[] buffer) {
if (audioHandler != null) {
audioHandler.audioWriteShortBuffer(buffer);
}
}
static void audioWriteByteBuffer(final byte[] buffer) {
if (audioHandler != null) {
audioHandler.audioWriteByteBuffer(buffer);
}
}
static int captureOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, final int desiredFrames) {
if (audioHandler != null) {
return audioHandler.captureOpen(sampleRate, is16Bit, isStereo, desiredFrames);
} else {
return -1;
}
}
static int captureReadShortBuffer(final short[] buffer, final boolean blocking) {
if (audioHandler != null) {
return audioHandler.captureReadShortBuffer(buffer, blocking);
} else {
return -1;
}
}
static int captureReadByteBuffer(final byte[] buffer, final boolean blocking) {
if (audioHandler != null) {
return audioHandler.captureReadByteBuffer(buffer, blocking);
} else {
return -1;
}
}
static void audioClose() {
if (audioHandler != null) {
audioHandler.audioClose();
}
}
static void captureClose() {
if (audioHandler != null) {
audioHandler.captureClose();
}
}
/* CONTROLLER FUNCTIONS CALLED BY NATIVE THREADS */
static void pollInputDevices() {
if (controllerHandler != null) {
controllerHandler.pollInputDevices();
}
}
static void pollHapticDevices() {
if (controllerHandler != null) {
controllerHandler.pollHapticDevices();
}
}
static void hapticRun(final int deviceId, final int length) {
if (controllerHandler != null) {
controllerHandler.hapticRun(deviceId, length);
}
}
/* PLAYER FUNCTIONS CALLED BY NATIVE THREADS */
static boolean setActivityTitle(final String title) {
if (playerManager != null) {
return playerManager.setActivityTitle(title);
} else {
return true;
}
}
static void setWindowStyle(final boolean fullScreen) {
if (playerManager != null) {
playerManager.setWindowStyle(fullScreen);
}
}
static void setOrientation(final int w, final int h, final boolean resizable, final String hint) {
if (playerManager != null) {
playerManager.setOrientation(w, h, resizable, hint);
}
}
static boolean isScreenKeyboardShown() {
if (playerManager != null) {
return playerManager.isScreenKeyboardShown();
} else {
return false;
}
}
static boolean sendMessage(final int command, final int param) {
if (playerManager != null) {
return playerManager.sendMessage(command, param);
} else {
return false;
}
}
static Context getContext() {
if (playerManager != null) {
return playerManager.getContext();
} else {
return null;
}
}
static boolean isAndroidTV() {
if (playerManager != null) {
return playerManager.isAndroidTV();
} else {
return false;
}
}
static DisplayMetrics getDisplayDPI() {
if (playerManager != null) {
return playerManager.getDisplayDPI();
} else {
return null;
}
}
static boolean getManifestEnvironmentVariables() {
if (playerManager != null) {
return playerManager.getManifestEnvironmentVariables();
} else {
return false;
}
}
static boolean showTextInput(final int x, final int y, final int w, final int h) {
if (playerManager != null) {
return playerManager.showTextInput(x, y, w, h);
} else {
return false;
}
}
static Surface getNativeSurface() {
if (playerManager != null) {
return playerManager.getNativeSurface();
} else {
return null;
}
}
static int[] inputGetInputDeviceIds(final int sources) {
if (playerManager != null) {
return playerManager.inputGetInputDeviceIds(sources);
} else {
return new int[0];
}
}
static boolean clipboardHasText() {
if (playerManager != null) {
return playerManager.clipboardHasText();
} else {
return false;
}
}
static String clipboardGetText() {
if (playerManager != null) {
return playerManager.clipboardGetText();
} else {
return "";
}
}
static void clipboardSetText(final String string) {
if (playerManager != null) {
playerManager.clipboardSetText(string);
}
}
static InputStream openAPKExpansionInputStream(final String fileName) throws IOException {
Log.e(TAG, "Opening APK Expansion is not supported.");
return null;
}
static int messageBoxShowMessageBox(final int flags, final String title, final String message, final int[] buttonFlags, final int[] buttonIds, final String[] buttonTexts, final int[] colors) {
if (playerManager != null) {
return playerManager.showMessageBox(flags, title, message, buttonFlags, buttonIds, buttonTexts, colors);
} else {
return -1;
}
}
/* NATIVE SDL FUNCTIONS */
/**
* <p>Initializes SDL for FFplay. It must be called before other SDL functions.
*/
native static void nativeSDLInit();
/* NATIVE PLAYER FUNCTIONS */
native static int nativePlayerInit();
/**
* <p>Synchronously executes FFplay natively with arguments provided.
*
* @param arguments FFplay command options/arguments as string array
* @return zero on successful execution, 255 on user cancel and non-zero on error
*/
native static int nativePlayerRun(final String[] arguments);
native static void nativePlayerNativeLowMemory();
native static void nativePlayerNativeQuit();
native static void nativePlayerNativePause();
native static void nativePlayerNativeResume();
native static void nativePlayerOnDropFile(final String filename);
native static void nativePlayerOnResize(final int x, final int y, final int format, final float rate);
native static void nativePlayerOnKeyDown(final int keyCode);
native static void nativePlayerOnKeyUp(final int keyCode);
native static void nativePlayerOnKeyboardFocusLost();
native static void nativePlayerOnMouse(final int button, final int action, final float x, final float y);
native static void nativePlayerOnTouch(final int touchDevId, final int pointerFingerId, final int action, final float x, final float y, final float p);
native static void nativePlayerOnAccel(final float x, final float y, final float z);
native static void nativePlayerOnClipboardChanged();
native static void nativePlayerOnSurfaceChanged();
native static void nativePlayerOnSurfaceDestroyed();
native static String nativePlayerNativeGetHint(final String name);
native static void nativePlayerNativeSetenv(final String name, final String value);
/* NATIVE AUDIO FUNCTIONS */
native static int nativeAudioInit();
/* NATIVE CONTROLLER FUNCTIONS */
native static int nativeControllerInit();
native static int nativeControllerAddJoystick(final int deviceId, final String name, final String desc, final int isAccelerometer, final int nButtons, final int nAxes, final int nHats, final int nBalls);
native static int nativeControllerRemoveJoystick(final int deviceId);
native static int nativeControllerAddHaptic(final int deviceId, final String name);
native static int nativeControllerRemoveHaptic(final int deviceId);
native static int nativeControllerOnPadDown(final int deviceId, final int keycode);
native static int nativeControllerOnPadUp(final int deviceId, final int keycode);
native static void nativeControllerOnJoy(final int deviceId, final int axis, final float value);
native static void nativeControllerOnHat(final int deviceId, final int hatId, final int x, final int y);
/* NATIVE INPUT FUNCTIONS */
native static void nativeInputCommitText(final String text, final int newCursorPosition);
native static void nativeInputGenerateScancodeForUnichar(final char c);
native static void nativeInputSetComposingText(final String text, final int newCursorPosition);
}
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg;
import android.util.Log;
/**
* <p>Main class for FFprobe operations. Provides {@link #execute(String...)} method to execute
* FFprobe commands.
* <pre>
* int rc = FFprobe.execute("-hide_banner -v error -show_entries format=size -of default=noprint_wrappers=1 file1.mp4");
* Log.i(Config.TAG, String.format("Command execution %s.", (rc == 0?"completed successfully":"failed with rc=" + rc));
* </pre>
*
* @author Taner Sener
* @since v4.3.1
*/
public class FFprobe {
static {
AbiDetect.class.getName();
Config.class.getName();
}
/**
* Default constructor hidden.
*/
private FFprobe() {
}
/**
* <p>Synchronously executes FFprobe with arguments provided.
*
* @param arguments FFprobe command options/arguments as string array
* @return zero on successful execution, 255 on user cancel and non-zero on error
*/
public static int execute(final String[] arguments) {
final int lastReturnCode = Config.nativeFFprobeExecute(arguments);
Config.setLastReturnCode(lastReturnCode);
return lastReturnCode;
}
/**
* <p>Synchronously executes FFprobe command provided. Space character is used to split command
* into arguments. You can use single and double quote characters to specify arguments inside
* your command.
*
* @param command FFprobe command
* @return zero on successful execution, 255 on user cancel and non-zero on error
*/
public static int execute(final String command) {
return execute(FFmpeg.parseArguments(command));
}
/**
* <p>Returns media information for given file.
*
* <p>This method does not support executing multiple concurrent operations. If you execute
* multiple operations (execute or getMediaInformation) at the same time, the response of this
* method is not predictable.
*
* @param path path or uri of media file
* @return media information
* @since 3.0
*/
public static MediaInformation getMediaInformation(final String path) {
final int rc = execute(new String[]{"-v", "info", "-hide_banner", "-i", path});
if (rc == 0) {
return MediaInformationParser.from(Config.getLastCommandOutput());
} else {
Log.i(Config.TAG, Config.getLastCommandOutput());
return null;
}
}
/**
* <p>Returns media information for given file.
*
* <p>This method does not support executing multiple concurrent operations. If you execute
* multiple operations (execute or getMediaInformation) at the same time, the response of this
* method is not predictable.
*
* @param path path or uri of media file
* @param timeout complete timeout
* @return media information
* @since 3.0
* @deprecated this method is deprecated since v4.3.1. You can still use this method but
* <code>timeout</code> parameter is not effective anymore.
*/
public static MediaInformation getMediaInformation(final String path, final Long timeout) {
return getMediaInformation(path);
}
}
@@ -27,6 +27,14 @@ package com.arthenica.mobileffmpeg;
*/
public enum Level {
/**
* This log level is defined by MobileFFmpeg. It is used to specify logs printed to stderr by
* ffmpeg. Logs that has this level are not filtered and always redirected.
*
* @since 4.3.1
*/
AV_LOG_STDERR(-16),
/**
* Print no output.
*/
@@ -85,7 +93,9 @@ public enum Level {
* @return enumeration defined by value
*/
public static Level from(final int value) {
if (value == AV_LOG_QUIET.getValue()) {
if (value == AV_LOG_STDERR.getValue()) {
return AV_LOG_STDERR;
} else if (value == AV_LOG_QUIET.getValue()) {
return AV_LOG_QUIET;
} else if (value == AV_LOG_PANIC.getValue()) {
return AV_LOG_PANIC;
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.player;
public interface AudioHandler {
void initialize();
int audioOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, final int desiredFrames);
void audioWriteShortBuffer(final short[] buffer);
void audioWriteByteBuffer(final byte[] buffer);
int captureOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, final int desiredFrames);
int captureReadShortBuffer(final short[] buffer, final boolean blocking);
int captureReadByteBuffer(final byte[] buffer, final boolean blocking);
void audioClose();
void captureClose();
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018 Taner Sener
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
@@ -17,20 +17,23 @@
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.test;
package com.arthenica.mobileffmpeg.player;
import org.junit.Test;
import android.content.Context;
import android.view.MotionEvent;
import static org.junit.Assert.*;
public interface ControllerHandler {
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
void initialize(final Context context);
boolean handleJoystickMotionEvent(final MotionEvent event);
void pollInputDevices();
void pollHapticDevices();
void hapticRun(final int deviceId, final int length);
boolean isDeviceSDLJoystick(final int deviceId);
}
@@ -0,0 +1,180 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* CHANGES 07.2020
* - SDLActivity renamed as FullScreenActivity
*/
package com.arthenica.mobileffmpeg.player;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import com.arthenica.mobileffmpeg.FFplay;
import static com.arthenica.mobileffmpeg.Config.TAG;
import static com.arthenica.mobileffmpeg.player.PlayerSession.FFPLAY_COMMAND;
import static com.arthenica.mobileffmpeg.player.PlayerSession.NativeState;
public class FullScreenActivity extends Activity {
protected PlayerSurface playerSurface;
protected ViewGroup viewLayout;
protected PlayerSession playerSession;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String ffplayCommand = intent.getStringExtra(FFPLAY_COMMAND);
if (ffplayCommand == null) {
Log.i(TAG, "FullScreenActivity created with empty ffplay command.");
} else {
Log.v(TAG, "FullScreenActivity created.");
}
playerSession = new PlayerSession(getRequestedOrientation(), this, ffplayCommand);
playerSurface = new PlayerSurface(this);
playerSurface.init(this, new GenericMotionListener(), playerSession);
viewLayout = new RelativeLayout(this);
viewLayout.addView(playerSurface);
setContentView(viewLayout);
setWindowStyle(false);
}
@Override
protected void onPause() {
Log.v(TAG, "FullScreenActivity paused.");
setNextNativeState(NativeState.PAUSED);
setResumedCalled(false);
handleNativeState();
super.onPause();
}
@Override
protected void onResume() {
Log.v(TAG, "FullScreenActivity resumed.");
setNextNativeState(NativeState.RESUMED);
setResumedCalled(true);
handleNativeState();
super.onResume();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
Log.v(TAG, String.format("FullScreenActivity window focus changed, hasFocus: %s.", hasFocus));
setHasFocus(hasFocus);
if (hasFocus) {
setNextNativeState(NativeState.RESUMED);
} else {
setNextNativeState(NativeState.PAUSED);
}
handleNativeState();
super.onWindowFocusChanged(hasFocus);
}
@Override
public void onLowMemory() {
Log.v(TAG, "FullScreenActivity is on low memory.");
FFplay.playerNativeLowMemory();
super.onLowMemory();
}
@Override
protected void onDestroy() {
Log.v(TAG, "FullScreenActivity destroyed.");
setNextNativeState(NativeState.PAUSED);
handleNativeState();
// Send a quit message to the application
FFplay.playerNativeQuit();
super.onDestroy();
}
@Override
public boolean dispatchKeyEvent(final KeyEvent event) {
int keyCode = event.getKeyCode();
// Ignore certain special keys so they're handled by Android
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
keyCode == KeyEvent.KEYCODE_CAMERA ||
keyCode == KeyEvent.KEYCODE_ZOOM_IN ||
keyCode == KeyEvent.KEYCODE_ZOOM_OUT) {
return false;
}
return super.dispatchKeyEvent(event);
}
protected void setNextNativeState(final NativeState nextNativeState) {
PlayerSession playerSession = this.playerSession;
if (playerSession != null) {
playerSession.setNextNativeState(nextNativeState);
}
}
protected void handleNativeState() {
PlayerSurface playerSurface = this.playerSurface;
if (playerSurface != null) {
playerSurface.handleNativeState();
}
}
protected void setHasFocus(final boolean hasFocus) {
PlayerSurface playerSurface = this.playerSurface;
if (playerSurface != null) {
playerSurface.setHasFocus(hasFocus);
}
}
protected void setResumedCalled(final boolean resumedCalled) {
PlayerSurface playerSurface = this.playerSurface;
if (playerSurface != null) {
playerSurface.setResumedCalled(resumedCalled);
}
}
protected void setWindowStyle(final boolean fullScreen) {
PlayerSurface playerSurface = this.playerSurface;
if (playerSurface != null) {
playerSurface.setWindowStyle(fullScreen);
}
}
public PlayerSurface getPlayerSurface() {
return playerSurface;
}
public PlayerSession getPlayerSession() {
return playerSession;
}
}
@@ -0,0 +1,191 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* CHANGES 07.2020
* - SDLAudioHandler renamed as GenericAudioHandler
*/
package com.arthenica.mobileffmpeg.player;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
import static com.arthenica.mobileffmpeg.Config.TAG;
public class GenericAudioHandler implements AudioHandler {
protected AudioTrack audioTrack;
protected AudioRecord audioRecord;
public void initialize() {
audioTrack = null;
audioRecord = null;
}
public int audioOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, int desiredFrames) {
int channelConfig = isStereo ? AudioFormat.CHANNEL_OUT_STEREO : AudioFormat.CHANNEL_OUT_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
Log.v(TAG, String.format("AudioHandler audio: wanted %s %s %skHz, %d frames buffer.", isStereo ? "stereo" : "mono", is16Bit ? "16-bit" : "8-bit", sampleRate / 1000f, desiredFrames));
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
if (audioTrack == null) {
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Log.e(TAG, "AudioHandler failed during initialization of AudioTrack.");
audioTrack = null;
return -1;
}
audioTrack.play();
}
Log.v(TAG, String.format("AudioHandler audio: got %s %s %skHz, %d frames buffer.", (audioTrack.getChannelCount() >= 2) ? "stereo" : "mono", (audioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit", audioTrack.getSampleRate() / 1000f, desiredFrames));
return 0;
}
public void audioWriteShortBuffer(final short[] buffer) {
if (audioTrack == null) {
Log.e(TAG, "AudioHandler attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = audioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "AudioHandler audio: error return from write(short).");
return;
}
}
}
public void audioWriteByteBuffer(final byte[] buffer) {
if (audioTrack == null) {
Log.e(TAG, "AudioHandler attempted to make audio call with uninitialized audio!");
return;
}
for (int i = 0; i < buffer.length; ) {
int result = audioTrack.write(buffer, i, buffer.length - i);
if (result > 0) {
i += result;
} else if (result == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// Nom nom
}
} else {
Log.w(TAG, "AudioHandler audio: error return from write(byte).");
return;
}
}
}
public int captureOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, int desiredFrames) {
int channelConfig = isStereo ? AudioFormat.CHANNEL_IN_STEREO : AudioFormat.CHANNEL_IN_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
Log.v(TAG, String.format("AudioHandler capture: wanted %s %s %skHz, %d frames buffer.", isStereo ? "stereo" : "mono", is16Bit ? "16-bit" : "8-bit", sampleRate / 1000f, desiredFrames));
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
if (audioRecord == null) {
audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize);
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "AudioHandler failed during initialization of AudioRecord.");
audioRecord.release();
audioRecord = null;
return -1;
}
audioRecord.startRecording();
}
Log.v(TAG, String.format("AudioHandler capture: got %s %s %skHz, %d frames buffer.", (audioRecord.getChannelCount() >= 2) ? "stereo" : "mono", (audioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit", audioRecord.getSampleRate() / 1000f, desiredFrames));
return 0;
}
public int captureReadShortBuffer(final short[] buffer, final boolean blocking) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return audioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
} else {
return audioRecord.read(buffer, 0, buffer.length);
}
}
public int captureReadByteBuffer(final byte[] buffer, final boolean blocking) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return audioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
} else {
return audioRecord.read(buffer, 0, buffer.length);
}
}
public void audioClose() {
if (audioTrack != null) {
audioTrack.stop();
audioTrack.release();
audioTrack = null;
}
}
public void captureClose() {
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
}
}
@@ -0,0 +1,110 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
package com.arthenica.mobileffmpeg.player;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import static com.arthenica.mobileffmpeg.Config.TAG;
/**
* A Handler class for Messages from native SDL applications.
* It uses current Activities as target (e.g. for the title).
* static to prevent implicit references to enclosing object.
*/
public class GenericCommandHandler extends Handler {
public static final int COMMAND_CHANGE_TITLE = 1;
public static final int COMMAND_CHANGE_WINDOW_STYLE = 2;
public static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
protected final Context context;
public GenericCommandHandler(final Context context) {
this.context = context;
}
@Override
public void handleMessage(final Message message) {
switch (message.arg1) {
case COMMAND_CHANGE_TITLE:
if (context instanceof Activity) {
((Activity) context).setTitle((String) message.obj);
} else {
Log.e(TAG, "CommandHandler error handling message, getContext() returned no Activity.");
}
break;
case COMMAND_CHANGE_WINDOW_STYLE:
if (Build.VERSION.SDK_INT < 19) {
// This version of Android doesn't support the immersive fullscreen mode
break;
}
/* This needs more testing, per bug 4096 - Enabling fullscreen on Android causes the app to toggle fullscreen mode continuously in a loop
***
if (context instanceof Activity) {
Window window = ((Activity) context).getWindow();
if (window != null) {
if ((message.obj instanceof Integer) && (((Integer) message.obj).intValue() != 0)) {
int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
window.getDecorView().setSystemUiVisibility(flags);
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
window.getDecorView().setSystemUiVisibility(flags);
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
} else {
Log.e(TAG, "error handling message, getContext() returned no Activity");
}
***/
break;
case COMMAND_SET_KEEP_SCREEN_ON: {
if (context instanceof Activity) {
Window window = ((Activity) context).getWindow();
if (window != null) {
if ((message.obj instanceof Integer) && (((Integer) message.obj).intValue() != 0)) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
}
break;
}
default:
Log.e(TAG, String.format("CommandHandler error handling message, command is %d.", message.arg1));
}
}
}
@@ -0,0 +1,81 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* CHANGES 07.2020
* - SDLControllerHandler renamed as GenericControllerHandler
*/
package com.arthenica.mobileffmpeg.player;
import android.content.Context;
import android.view.InputDevice;
import android.view.MotionEvent;
public class GenericControllerHandler implements ControllerHandler {
protected GenericJoystickHandler genericJoystickHandler;
protected GenericHapticHandler genericHapticHandler;
public void initialize(final Context context) {
genericJoystickHandler = new GenericJoystickHandler(this);
genericHapticHandler = new GenericHapticHandler(context);
}
/**
* Joystick glue code, just a series of stubs that redirect to the JoystickHandler instance.
*/
public boolean handleJoystickMotionEvent(final MotionEvent event) {
return genericJoystickHandler.handleMotionEvent(event);
}
public void pollInputDevices() {
genericJoystickHandler.pollInputDevices();
}
public void pollHapticDevices() {
genericHapticHandler.pollHapticDevices();
}
public void hapticRun(final int deviceId, final int length) {
genericHapticHandler.run(deviceId, length);
}
/**
* Check if a given device is considered a possible SDL joystick.
*
* @param deviceId device identifier
* @return true if device is a joystick, false otherwise
*/
public boolean isDeviceSDLJoystick(final int deviceId) {
InputDevice device = InputDevice.getDevice(deviceId);
if ((device == null) || device.isVirtual() || (deviceId < 0)) {
return false;
}
int sources = device.getSources();
return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
);
}
}
@@ -0,0 +1,148 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* CHANGES 07.2020
* - SDLHapticHandler renamed as GenericHapticHandler
* - SDLHaptic class renamed as Haptic
* - Haptic class refactored
*/
package com.arthenica.mobileffmpeg.player;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Vibrator;
import android.view.InputDevice;
import com.arthenica.mobileffmpeg.FFplay;
import java.util.ArrayList;
/**
* <p>Generic haptic handler for FFplay Controller.
*/
public class GenericHapticHandler {
public static class Haptic {
public final int deviceId;
public final String name;
public final Vibrator vibrator;
public Haptic(final int deviceId, final String name, final Vibrator vibrator) {
this.deviceId = deviceId;
this.name = name;
this.vibrator = vibrator;
}
}
protected final ArrayList<Haptic> hapticList;
protected final Vibrator vibratorService;
public GenericHapticHandler(final Context context) {
hapticList = new ArrayList<>();
vibratorService = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
}
@SuppressLint("MissingPermission")
public void run(final int deviceId, final int length) {
Haptic haptic = getHaptic(deviceId);
if (haptic != null) {
haptic.vibrator.vibrate(length);
}
}
public void pollHapticDevices() {
final int DEVICE_ID_VIBRATOR_SERVICE = 999999;
boolean hasVibratorService = false;
final int[] deviceIdArray = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for (int i = deviceIdArray.length - 1; i > -1; i--) {
Haptic haptic = getHaptic(deviceIdArray[i]);
if (haptic == null) {
InputDevice device = InputDevice.getDevice(deviceIdArray[i]);
Vibrator vib = device.getVibrator();
if (vib.hasVibrator()) {
haptic = new Haptic(deviceIdArray[i], device.getName(), vib);
hapticList.add(haptic);
FFplay.controllerAddHaptic(haptic.deviceId, haptic.name);
}
}
}
/* Check VIBRATOR_SERVICE */
if (vibratorService != null) {
hasVibratorService = vibratorService.hasVibrator();
if (hasVibratorService) {
Haptic haptic = getHaptic(DEVICE_ID_VIBRATOR_SERVICE);
if (haptic == null) {
haptic = new Haptic(DEVICE_ID_VIBRATOR_SERVICE, "VIBRATOR_SERVICE", vibratorService);
hapticList.add(haptic);
FFplay.controllerAddHaptic(haptic.deviceId, haptic.name);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<>();
for (int i = 0; i < hapticList.size(); i++) {
int deviceId = hapticList.get(i).deviceId;
int j;
for (j = 0; j < deviceIdArray.length; j++) {
if (deviceId == deviceIdArray[j]) break;
}
if (deviceId == DEVICE_ID_VIBRATOR_SERVICE && hasVibratorService) {
// don't remove the vibrator if it is still present
} else if (j == deviceIdArray.length) {
removedDevices.add(deviceId);
}
}
for (int i = 0; i < removedDevices.size(); i++) {
int deviceId = removedDevices.get(i);
FFplay.controllerRemoveHaptic(deviceId);
for (int j = 0; j < hapticList.size(); j++) {
if (hapticList.get(j).deviceId == deviceId) {
hapticList.remove(j);
break;
}
}
}
}
protected Haptic getHaptic(final int deviceId) {
for (int i = 0; i < hapticList.size(); i++) {
if (hapticList.get(i).deviceId == deviceId) {
return hapticList.get(i);
}
}
return null;
}
}
@@ -0,0 +1,179 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* CHANGES 07.2020
* - SDLJoystickHandler renamed as GenericJoystickHandler
* - SDLJoystick class renamed as Joystick
* - Joystick class refactored
*/
package com.arthenica.mobileffmpeg.player;
import android.view.InputDevice;
import android.view.MotionEvent;
import com.arthenica.mobileffmpeg.FFplay;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* <p>Generic joystick handler for FFplay Controller.
*/
public class GenericJoystickHandler {
public static class Joystick {
public final int deviceId;
public final String name;
public final String desc;
public final ArrayList<InputDevice.MotionRange> axes;
public final ArrayList<InputDevice.MotionRange> hats;
public Joystick(final int deviceId, final String name, final String desc, final ArrayList<InputDevice.MotionRange> axes, final ArrayList<InputDevice.MotionRange> hats) {
this.deviceId = deviceId;
this.name = name;
this.desc = desc;
this.axes = axes;
this.hats = hats;
}
}
public static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
public int compare(final InputDevice.MotionRange arg0, final InputDevice.MotionRange arg1) {
return arg0.getAxis() - arg1.getAxis();
}
}
protected final ArrayList<Joystick> joystickList;
protected final ControllerHandler controllerHandler;
public GenericJoystickHandler(final ControllerHandler controllerHandler) {
this.joystickList = new ArrayList<>();
this.controllerHandler = controllerHandler;
}
public void pollInputDevices() {
int[] deviceIds = InputDevice.getDeviceIds();
// It helps processing the device ids in reverse order
// For example, in the case of the XBox 360 wireless dongle,
// so the first controller seen by SDL matches what the receiver
// considers to be the first controller
for (int i = deviceIds.length - 1; i > -1; i--) {
Joystick joystick = getJoystick(deviceIds[i]);
if (joystick == null) {
InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
if (controllerHandler.isDeviceSDLJoystick(deviceIds[i])) {
joystick = new Joystick(deviceIds[i], joystickDevice.getName(), getJoystickDescriptor(joystickDevice), new ArrayList<InputDevice.MotionRange>(), new ArrayList<InputDevice.MotionRange>());
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
for (InputDevice.MotionRange range : ranges) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
joystick.hats.add(range);
} else {
joystick.axes.add(range);
}
}
}
joystickList.add(joystick);
FFplay.controllerAddJoystick(joystick.deviceId, joystick.name, joystick.desc, 0, -1, joystick.axes.size(), joystick.hats.size() / 2, 0);
}
}
}
/* Check removed devices */
ArrayList<Integer> removedDevices = new ArrayList<>();
for (int i = 0; i < joystickList.size(); i++) {
int deviceId = joystickList.get(i).deviceId;
int j;
for (j = 0; j < deviceIds.length; j++) {
if (deviceId == deviceIds[j]) break;
}
if (j == deviceIds.length) {
removedDevices.add(deviceId);
}
}
for (int i = 0; i < removedDevices.size(); i++) {
int deviceId = removedDevices.get(i);
FFplay.controllerRemoveJoystick(deviceId);
for (int j = 0; j < joystickList.size(); j++) {
if (joystickList.get(j).deviceId == deviceId) {
joystickList.remove(j);
break;
}
}
}
}
protected Joystick getJoystick(int deviceId) {
for (int i = 0; i < joystickList.size(); i++) {
if (joystickList.get(i).deviceId == deviceId) {
return joystickList.get(i);
}
}
return null;
}
public boolean handleMotionEvent(final MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
int actionPointerIndex = event.getActionIndex();
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_MOVE) {
Joystick joystick = getJoystick(event.getDeviceId());
if (joystick != null) {
for (int i = 0; i < joystick.axes.size(); i++) {
InputDevice.MotionRange range = joystick.axes.get(i);
/* Normalize the value to -1...1 */
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
FFplay.controllerOnJoy(joystick.deviceId, i, value);
}
for (int i = 0; i < joystick.hats.size(); i += 2) {
int hatX = Math.round(event.getAxisValue(joystick.hats.get(i).getAxis(), actionPointerIndex));
int hatY = Math.round(event.getAxisValue(joystick.hats.get(i + 1).getAxis(), actionPointerIndex));
FFplay.controllerOnHat(joystick.deviceId, i / 2, hatX, hatY);
}
}
}
}
return true;
}
public String getJoystickDescriptor(final InputDevice joystickDevice) {
String desc = joystickDevice.getDescriptor();
if (desc != null && !desc.isEmpty()) {
return desc;
}
return joystickDevice.getName();
}
}
@@ -0,0 +1,90 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* CHANGES 07.2020
* - SDLGenericMotionListener renamed as GenericMotionListener
*/
package com.arthenica.mobileffmpeg.player;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import com.arthenica.mobileffmpeg.FFplay;
/**
* <p>Generic motion listener for FFplay Player.
*/
public class GenericMotionListener implements View.OnGenericMotionListener {
@Override
public boolean onGenericMotion(final View view, final MotionEvent event) {
ControllerHandler controllerHandler = FFplay.getControllerHandler();
float x, y;
int action;
switch (event.getSource()) {
case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_DPAD: {
if (controllerHandler != null) {
return controllerHandler.handleJoystickMotionEvent(event);
} else {
return false;
}
}
case InputDevice.SOURCE_MOUSE: {
if (!FFplay.isSeparateMouseAndTouch()) {
break;
}
action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_SCROLL: {
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
FFplay.playerOnMouse(0, action, x, y);
return true;
}
case MotionEvent.ACTION_HOVER_MOVE: {
x = event.getX(0);
y = event.getY(0);
FFplay.playerOnMouse(0, action, x, y);
return true;
}
default: {
break;
}
}
break;
}
default: {
break;
}
}
return false;
}
}
@@ -0,0 +1,92 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* CHANGES 07.2020
* - SDLClipboardHandler renamed as PlayerClipboard
* - Added null checks for clipboardManager field
*/
package com.arthenica.mobileffmpeg.player;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import com.arthenica.mobileffmpeg.FFplay;
/**
* <p>Clipboard for FFplay Player.
*/
public class PlayerClipboard implements ClipboardManager.OnPrimaryClipChangedListener {
protected final Context context;
protected final ClipboardManager clipboardManager;
public PlayerClipboard(final Context context) {
this.context = context;
this.clipboardManager = (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if (this.clipboardManager != null) {
this.clipboardManager.addPrimaryClipChangedListener(this);
}
}
public boolean clipboardHasText() {
if (clipboardManager != null) {
final ClipData clip = clipboardManager.getPrimaryClip();
return (clip != null) && (clip.getItemCount() > 0);
}
return false;
}
public String clipboardGetText() {
CharSequence text = null;
if (clipboardManager != null) {
ClipData clip = clipboardManager.getPrimaryClip();
if (clip != null && clip.getItemCount() > 0) {
text = clip.getItemAt(0).coerceToText(context);
}
}
if (text != null) {
return text.toString();
} else {
return null;
}
}
public void clipboardSetText(final String string) {
if (clipboardManager != null) {
clipboardManager.removePrimaryClipChangedListener(this);
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, string));
clipboardManager.addPrimaryClipChangedListener(this);
}
}
@Override
public void onPrimaryClipChanged() {
FFplay.playerOnClipboardChanged();
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.player;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.Surface;
public interface PlayerManager {
void initialize();
boolean setActivityTitle(final String title);
void setWindowStyle(final boolean fullScreen);
void setOrientation(final int w, final int h, final boolean resizable, final String hint);
boolean isScreenKeyboardShown();
boolean sendMessage(final int command, final int param);
Context getContext();
boolean isAndroidTV();
DisplayMetrics getDisplayDPI();
boolean getManifestEnvironmentVariables();
boolean showTextInput(final int x, final int y, final int w, final int h);
Surface getNativeSurface();
int[] inputGetInputDeviceIds(final int sources);
boolean clipboardHasText();
String clipboardGetText();
void clipboardSetText(final String string);
int showMessageBox(final int flags, final String title, final String message, final int[] buttonFlags, final int[] buttonIds, final String[] buttonTexts, final int[] colors);
}
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2020 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.player;
import android.content.Context;
import android.os.AsyncTask;
import com.arthenica.mobileffmpeg.FFplay;
import com.arthenica.mobileffmpeg.util.AsyncSingleFFplayExecuteTask;
public class PlayerSession {
public static final String FFPLAY_COMMAND = "ffplayCommand";
public enum NativeState {
INIT, RESUMED, PAUSED
}
protected int requestedOrientation;
protected Context context;
protected final String command;
protected NativeState nextNativeState;
protected NativeState currentNativeState;
public PlayerSession(final int requestedOrientation, final Context context, final String command) {
this.requestedOrientation = requestedOrientation;
this.context = context;
this.command = command;
}
public NativeState getNextNativeState() {
return nextNativeState;
}
public void setNextNativeState(final NativeState nextNativeState) {
this.nextNativeState = nextNativeState;
}
public NativeState getCurrentNativeState() {
return currentNativeState;
}
public void setCurrentNativeState(final NativeState currentNativeState) {
this.currentNativeState = currentNativeState;
}
public Context getContext() {
return context;
}
public void setContext(final Context context) {
this.context = context;
}
public int getRequestedOrientation() {
return requestedOrientation;
}
public AsyncTask<String, Integer, Integer> execute() {
FFplay.setAudioHandler(new GenericAudioHandler());
FFplay.setControllerManager(new GenericControllerHandler());
AudioHandler audioHandler = FFplay.getAudioHandler();
if (audioHandler != null) {
audioHandler.initialize();
}
ControllerHandler controllerHandler = FFplay.getControllerHandler();
if (controllerHandler != null) {
controllerHandler.initialize(context);
}
AsyncSingleFFplayExecuteTask task = new AsyncSingleFFplayExecuteTask(command);
return task.execute("");
}
}
@@ -0,0 +1,683 @@
/*
* Simple DirectMedia Layer
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* CHANGES 07.2020
* - SDLSurface renamed as PlayerSurface
*/
package com.arthenica.mobileffmpeg.player;
import android.app.Activity;
import android.app.UiModeManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import com.arthenica.mobileffmpeg.FFplay;
import com.arthenica.mobileffmpeg.player.PlayerSession.NativeState;
import java.util.Arrays;
import static android.content.Context.UI_MODE_SERVICE;
import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
import static com.arthenica.mobileffmpeg.Config.TAG;
import static com.arthenica.mobileffmpeg.player.GenericCommandHandler.COMMAND_CHANGE_TITLE;
import static com.arthenica.mobileffmpeg.player.GenericCommandHandler.COMMAND_CHANGE_WINDOW_STYLE;
/**
* PlayerSurface. This is what we draw on, so we need to know when it's created in order to do
* anything useful.
* <p>
* Because of this, that's where we set up the SDL thread
*/
public class PlayerSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener, PlayerManager {
protected Activity activity;
protected Handler commandHandler;
protected PlayerClipboard playerClipboard;
protected SensorManager sensorManager;
protected Display display;
protected float width, height;
protected PlayerSession playerSession;
protected boolean ready;
protected boolean resumedCalled;
protected boolean hasFocus;
public PlayerSurface(final Context context) {
super(context);
onCreated(context);
}
public PlayerSurface(final Context context, final AttributeSet attrs) {
super(context, attrs);
onCreated(context);
}
public PlayerSurface(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
onCreated(context);
}
public PlayerSurface(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
onCreated(context);
}
protected void onCreated(final Context context) {
Log.v(TAG, String.format("PlayerSurface created on device: %s and model: %s.", android.os.Build.DEVICE, android.os.Build.MODEL));
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
commandHandler = new GenericCommandHandler(context);
playerClipboard = new PlayerClipboard(context);
display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
// Some arbitrary defaults to avoid a potential division by zero
width = 1.0f;
height = 1.0f;
ready = false;
}
public void init(final Activity activity, final View.OnGenericMotionListener motionListener, final PlayerSession playerSession) {
this.activity = activity;
setOnGenericMotionListener(motionListener);
FFplay.setPlayerManager(this);
this.playerSession = playerSession;
}
public void handlePause() {
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
}
public void handleResume() {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
public Surface getNativeSurface() {
return getHolder().getSurface();
}
@Override
public void surfaceCreated(final SurfaceHolder ignored) {
}
@Override
public void surfaceDestroyed(final SurfaceHolder holder) {
// Transition to pause, if needed
setNextNativeState(NativeState.PAUSED);
handleNativeState();
ready = false;
FFplay.playerOnSurfaceDestroyed();
}
@Override
public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
switch (format) {
case PixelFormat.A_8:
Log.v(TAG, "PlayerSurface using pixel format A_8");
break;
case PixelFormat.LA_88:
Log.v(TAG, "PlayerSurface using pixel format LA_88");
break;
case PixelFormat.L_8:
Log.v(TAG, "PlayerSurface using pixel format L_8");
break;
case PixelFormat.RGBA_4444:
Log.v(TAG, "PlayerSurface using pixel format RGBA_4444");
sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
break;
case PixelFormat.RGBA_5551:
Log.v(TAG, "PlayerSurface using pixel format RGBA_5551");
sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
break;
case PixelFormat.RGBA_8888:
Log.v(TAG, "PlayerSurface using pixel format RGBA_8888");
sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
break;
case PixelFormat.RGBX_8888:
Log.v(TAG, "PlayerSurface using pixel format RGBX_8888");
sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
break;
case PixelFormat.RGB_332:
Log.v(TAG, "PlayerSurface using pixel format RGB_332");
sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
break;
case PixelFormat.RGB_565:
Log.v(TAG, "PlayerSurface using pixel format RGB_565");
sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
break;
case PixelFormat.RGB_888:
Log.v(TAG, "PlayerSurface using pixel format RGB_888");
// Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
break;
default:
Log.v(TAG, String.format("PlayerSurface using pixel format unknown %d", format));
break;
}
this.width = width;
this.height = height;
FFplay.playerOnResize(width, height, sdlFormat, display.getRefreshRate());
Log.v(TAG, String.format("PlayerSurface window size: %dx%d.", width, height));
boolean skip = false;
int requestedOrientation = getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
// Accept any
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
if (this.width > this.height) {
skip = true;
}
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
if (this.width < this.height) {
skip = true;
}
}
// Special Patch for Square Resolution: Black Berry Passport
if (skip) {
double min = Math.min(this.width, this.height);
double max = Math.max(this.width, this.height);
if (max / min < 1.20) {
Log.v(TAG, "PlayerSurface don't skip on such aspect-ratio. Could be a square resolution.");
skip = false;
}
}
if (skip) {
Log.v(TAG, "PlayerSurface skip .. Surface is not ready.");
ready = false;
return;
}
/* Surface is ready */
ready = true;
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
FFplay.playerOnSurfaceChanged();
handleNativeState();
}
@Override
public boolean onKey(final View v, final int keyCode, final KeyEvent event) {
final ControllerHandler controllerHandler = FFplay.getControllerHandler();
// Dispatch the different events depending on where they come from
// Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
// So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
//
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source
// So, retrieve the device itself and check all of its sources
if (controllerHandler != null && controllerHandler.isDeviceSDLJoystick(event.getDeviceId())) {
// Note that we process events with specific key codes here
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (FFplay.controllerOnPadDown(event.getDeviceId(), keyCode) == 0) {
return true;
}
} else if (event.getAction() == KeyEvent.ACTION_UP) {
if (FFplay.controllerOnPadUp(event.getDeviceId(), keyCode) == 0) {
return true;
}
}
}
if ((event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (isTextInputEvent(event)) {
FFplay.inputCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
}
FFplay.playerOnKeyDown(keyCode);
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
FFplay.playerOnKeyUp(keyCode);
return true;
}
}
if ((event.getSource() & InputDevice.SOURCE_MOUSE) != 0) {
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
// they are ignored here because sending them as mouse input to SDL is messy
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
case KeyEvent.ACTION_UP:
// mark the event as handled or it will be handled by system
// handling KEYCODE_BACK by system will call onBackPressed()
return true;
}
}
}
return false;
}
@Override
public boolean onTouch(final View v, final MotionEvent event) {
final int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId;
int mouseButton;
int i = -1;
float x, y, p;
if (event.getSource() == InputDevice.SOURCE_MOUSE && FFplay.isSeparateMouseAndTouch()) {
try {
mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
} catch (Exception e) {
mouseButton = 1; // oh well.
}
FFplay.playerOnMouse(mouseButton, action, event.getX(0), event.getY(0));
} else {
switch (action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / width;
y = event.getY(i) / height;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
FFplay.playerOnTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
i = 0;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / width;
y = event.getY(i) / height;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
FFplay.playerOnTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / width;
y = event.getY(i) / height;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
FFplay.playerOnTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
default:
break;
}
}
return true;
}
@Override
public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
// TODO
}
@Override
public void onSensorChanged(final SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float x, y;
switch (display.getRotation()) {
case Surface.ROTATION_90:
x = -event.values[1];
y = event.values[0];
break;
case Surface.ROTATION_270:
x = event.values[1];
y = -event.values[0];
break;
case Surface.ROTATION_180:
x = -event.values[1];
y = -event.values[0];
break;
default:
x = event.values[0];
y = event.values[1];
break;
}
FFplay.playerOnAccel(-x / SensorManager.GRAVITY_EARTH,
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH);
}
}
public void enableSensor(final int sensorType, final boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(sensorType),
SensorManager.SENSOR_DELAY_GAME, null);
} else {
sensorManager.unregisterListener(this, sensorManager.getDefaultSensor(sensorType));
}
}
int getRequestedOrientation() {
final PlayerSession playerSession = this.playerSession;
if (playerSession == null) {
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
} else {
return playerSession.getRequestedOrientation();
}
}
public void setNextNativeState(final NativeState nextNativeState) {
final PlayerSession playerSession = this.playerSession;
if (playerSession != null) {
playerSession.setNextNativeState(nextNativeState);
}
}
public void setCurrentNativeState(final NativeState currentNativeState) {
final PlayerSession playerSession = this.playerSession;
if (playerSession != null) {
playerSession.setCurrentNativeState(currentNativeState);
}
}
public static boolean isTextInputEvent(final KeyEvent event) {
if (event.isCtrlPressed()) {
return false;
}
return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
}
public void handleNativeState() {
Log.v(TAG, String.format("PlayerSurface handling nativeState with ready: %s, hasFocus: %s, resumed: %s.", ready, hasFocus, resumedCalled));
NativeState currentNativeState = null;
NativeState nextNativeState = null;
if (playerSession != null) {
currentNativeState = playerSession.getCurrentNativeState();
nextNativeState = playerSession.getNextNativeState();
}
Log.v(TAG, String.format("PlayerSurface handling nativeState with current:%s, next: %s.", currentNativeState, nextNativeState));
if (nextNativeState == currentNativeState) {
// Already in same state, discard.
return;
}
// Try a transition to init state
if (nextNativeState == NativeState.INIT) {
setCurrentNativeState(nextNativeState);
return;
}
// Try a transition to paused state
if (nextNativeState == NativeState.PAUSED) {
FFplay.playerNativePause();
handlePause();
setCurrentNativeState(nextNativeState);
return;
}
// Try a transition to resumed state
if (nextNativeState == NativeState.RESUMED) {
play();
}
}
public void play() {
if (ready && resumedCalled) {
initialize();
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
if (playerSession != null) {
playerSession.execute();
}
FFplay.playerNativeResume();
handleResume();
setCurrentNativeState(NativeState.RESUMED);
} else {
Log.v(TAG, String.format("PlayerSurface play failed for ready:%s, hasFocus: %s, resumed: %s.", ready, hasFocus, resumedCalled));
}
}
public void setHasFocus(final boolean hasFocus) {
this.hasFocus = hasFocus;
}
public void setResumedCalled(final boolean resumedCalled) {
this.resumedCalled = resumedCalled;
}
public boolean sendCommand(final int command, final Object data) {
Message message = commandHandler.obtainMessage();
message.arg1 = command;
message.obj = data;
return commandHandler.sendMessage(message);
}
public void initialize() {
setHasFocus(true);
setNextNativeState(NativeState.INIT);
setCurrentNativeState(NativeState.INIT);
}
public boolean setActivityTitle(final String title) {
return sendCommand(COMMAND_CHANGE_TITLE, title);
}
public void setWindowStyle(final boolean fullScreen) {
sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullScreen ? 1 : 0);
}
public void setOrientation(final int w, final int h, final boolean resizable, final String hint) {
int orientation = -1;
if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) {
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
} else if (hint.contains("LandscapeRight")) {
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
} else if (hint.contains("LandscapeLeft")) {
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
} else if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) {
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
} else if (hint.contains("Portrait")) {
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
} else if (hint.contains("PortraitUpsideDown")) {
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
}
/* no valid hint */
if (orientation == -1) {
if (resizable) {
/* no fixed orientation */
} else {
if (w > h) {
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
} else {
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
}
}
}
Log.v(TAG, String.format("PlayerSurface set orientation:%d, width:%d, height:%d, resizable:%s and hint:%s.", orientation, w, h, resizable, hint));
if (orientation != -1) {
activity.setRequestedOrientation(orientation);
}
}
public boolean isScreenKeyboardShown() {
return false;
}
public boolean sendMessage(final int command, final int param) {
return sendCommand(command, param);
}
public boolean isAndroidTV() {
UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE);
return (uiModeManager.getCurrentModeType() == UI_MODE_TYPE_TELEVISION);
}
public DisplayMetrics getDisplayDPI() {
return getContext().getResources().getDisplayMetrics();
}
public boolean getManifestEnvironmentVariables() {
try {
ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = applicationInfo.metaData;
if (bundle == null) {
return false;
}
String prefix = "SDL_ENV.";
final int trimLength = prefix.length();
for (String key : bundle.keySet()) {
if (key.startsWith(prefix)) {
String name = key.substring(trimLength);
String value = bundle.get(key).toString();
FFplay.playerNativeSetenv(name, value);
}
}
return true;
} catch (final Exception e) {
Log.i(TAG, "PlayerSurface failed to set environment variables. " + e.toString());
}
return false;
}
public boolean showTextInput(final int x, final int y, final int w, final int h) {
return false;
}
public int[] inputGetInputDeviceIds(final int sources) {
int[] ids = InputDevice.getDeviceIds();
int[] filtered = new int[ids.length];
int used = 0;
for (int id : ids) {
InputDevice device = InputDevice.getDevice(id);
if ((device != null) && ((device.getSources() & sources) != 0)) {
filtered[used++] = device.getId();
}
}
return Arrays.copyOf(filtered, used);
}
public int showMessageBox(
final int flags,
final String title,
final String message,
final int[] buttonFlags,
final int[] buttonIds,
final String[] buttonTexts,
final int[] colors) {
Log.i(TAG, String.format("PlayerSurface was asked to showMessageBox for title: %s, message: %s and %d buttons: %s.", title, message, (buttonIds != null) ? buttonIds.length : 0, Arrays.toString(buttonTexts)));
return -1;
}
public boolean clipboardHasText() {
return playerClipboard.clipboardHasText();
}
public String clipboardGetText() {
return playerClipboard.clipboardGetText();
}
public void clipboardSetText(final String string) {
playerClipboard.clipboardSetText(string);
}
}
@@ -21,25 +21,27 @@ package com.arthenica.mobileffmpeg.util;
import android.os.AsyncTask;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFmpeg;
public class AsyncExecuteTask extends AsyncTask<String, Integer, Integer> {
public class AsyncSingleFFmpegExecuteTask extends AsyncTask<String, Integer, Integer> {
private final String command;
private final SingleExecuteCallback singleExecuteCallback;
private final ExecuteCallback executeCallback;
public AsyncExecuteTask(final ExecuteCallback executeCallback) {
this.executeCallback = executeCallback;
public AsyncSingleFFmpegExecuteTask(final String command, final SingleExecuteCallback singleExecuteCallback) {
this.command = command;
this.singleExecuteCallback = singleExecuteCallback;
}
@Override
protected Integer doInBackground(final String... arguments) {
return FFmpeg.execute(arguments[0]);
return FFmpeg.execute(command);
}
@Override
protected void onPostExecute(final Integer rc) {
if (executeCallback != null) {
executeCallback.apply(rc, FFmpeg.getLastCommandOutput());
if (singleExecuteCallback != null) {
singleExecuteCallback.apply(rc, Config.getLastCommandOutput());
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2019 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.util;
import android.os.AsyncTask;
import android.util.Log;
import com.arthenica.mobileffmpeg.FFplay;
import static com.arthenica.mobileffmpeg.Config.TAG;
public class AsyncSingleFFplayExecuteTask extends AsyncTask<String, Integer, Integer> {
private final String command;
public AsyncSingleFFplayExecuteTask(final String command) {
this.command = command;
}
@Override
protected Integer doInBackground(final String... ignored) {
Log.v(TAG, String.format("Running FFplay for %s.", command));
int rc = FFplay.execute(command.split(" "));
Log.v(TAG, String.format("Finished running FFplay for %s.", command));
return rc;
}
@Override
protected void onPostExecute(final Integer ignored) {
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2019 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.util;
import android.os.AsyncTask;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFprobe;
public class AsyncSingleFFprobeExecuteTask extends AsyncTask<String, Integer, Integer> {
private final String command;
private final SingleExecuteCallback singleExecuteCallback;
public AsyncSingleFFprobeExecuteTask(final String command, final SingleExecuteCallback singleExecuteCallback) {
this.command = command;
this.singleExecuteCallback = singleExecuteCallback;
}
@Override
protected Integer doInBackground(final String... arguments) {
return FFprobe.execute(command);
}
@Override
protected void onPostExecute(final Integer rc) {
if (singleExecuteCallback != null) {
singleExecuteCallback.apply(rc, Config.getLastCommandOutput());
}
}
}
@@ -21,26 +21,27 @@ package com.arthenica.mobileffmpeg.util;
import android.os.AsyncTask;
import com.arthenica.mobileffmpeg.FFmpeg;
import com.arthenica.mobileffmpeg.FFprobe;
import com.arthenica.mobileffmpeg.MediaInformation;
public class AsyncGetMediaInformationTask extends AsyncTask<String, MediaInformation, MediaInformation> {
public class AsyncSingleGetMediaInformationTask extends AsyncTask<String, MediaInformation, MediaInformation> {
private final String path;
private final SingleGetMediaInformationCallback singleGetMediaInformationCallback;
private final GetMediaInformationCallback getMediaInformationCallback;
public AsyncGetMediaInformationTask(final GetMediaInformationCallback getMediaInformationCallback) {
this.getMediaInformationCallback = getMediaInformationCallback;
public AsyncSingleGetMediaInformationTask(final String path, final SingleGetMediaInformationCallback singleGetMediaInformationCallback) {
this.path = path;
this.singleGetMediaInformationCallback = singleGetMediaInformationCallback;
}
@Override
protected MediaInformation doInBackground(final String... arguments) {
return FFmpeg.getMediaInformation(arguments[0]);
return FFprobe.getMediaInformation(path);
}
@Override
protected void onPostExecute(final MediaInformation mediaInformation) {
if (getMediaInformationCallback != null) {
getMediaInformationCallback.apply(mediaInformation);
if (singleGetMediaInformationCallback != null) {
singleGetMediaInformationCallback.apply(mediaInformation);
}
}
@@ -20,13 +20,13 @@
package com.arthenica.mobileffmpeg.util;
/**
* <p>Represents a callback function to receive execution result.
* <p>Represents a callback function to receive a single execution result.
*
* @author Taner Sener
* @since v2.1
*/
@FunctionalInterface
public interface ExecuteCallback {
public interface SingleExecuteCallback {
void apply(int returnCode, String executeOutput);
@@ -22,12 +22,12 @@ package com.arthenica.mobileffmpeg.util;
import com.arthenica.mobileffmpeg.MediaInformation;
/**
* <p>Represents a callback function to receive getMediaInformation result.
* <p>Represents a callback function to receive a single getMediaInformation result.
*
* @author Taner Sener
*/
@FunctionalInterface
public interface GetMediaInformationCallback {
public interface SingleGetMediaInformationCallback {
void apply(MediaInformation mediaInformation);
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
@@ -1,11 +0,0 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
@@ -19,8 +19,7 @@
package com.arthenica.mobileffmpeg;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
@@ -138,7 +137,7 @@ public class ConfigTest {
Collections.sort(enabledList);
Assert.assertNotNull(enabledList);
Assert.assertTrue(enabledList.size() == 1);
Assert.assertEquals(1, enabledList.size());
}
@Test
@@ -22,8 +22,7 @@ package com.arthenica.mobileffmpeg;
import com.arthenica.mobileffmpeg.util.Pair;
import com.arthenica.mobileffmpeg.util.Trio;
import junit.framework.Assert;
import org.junit.Assert;
import org.junit.Test;
public class MediaInformationTest {
@@ -82,16 +81,16 @@ public class MediaInformationTest {
@Test
public void parseVideoStreamDisplayAspectRatio() {
Assert.assertEquals(null, MediaInformationParser.parseVideoStreamDisplayAspectRatio(""));
Assert.assertEquals(null, MediaInformationParser.parseVideoStreamDisplayAspectRatio("544x184"));
Assert.assertNull(MediaInformationParser.parseVideoStreamDisplayAspectRatio(""));
Assert.assertNull(MediaInformationParser.parseVideoStreamDisplayAspectRatio("544x184"));
Assert.assertEquals("640:427", MediaInformationParser.parseVideoStreamDisplayAspectRatio("2560x1708 [SAR 1:1 DAR 640:427]"));
Assert.assertEquals("8:9", MediaInformationParser.parseVideoStreamDisplayAspectRatio("3840x4320 [SAR 1:1 DAR 8:9]"));
}
@Test
public void parseVideoStreamSampleAspectRatio() {
Assert.assertEquals(null, MediaInformationParser.parseVideoStreamSampleAspectRatio(""));
Assert.assertEquals(null, MediaInformationParser.parseVideoStreamSampleAspectRatio("544x184"));
Assert.assertNull(MediaInformationParser.parseVideoStreamSampleAspectRatio(""));
Assert.assertNull(MediaInformationParser.parseVideoStreamSampleAspectRatio("544x184"));
Assert.assertEquals("1:1", MediaInformationParser.parseVideoStreamSampleAspectRatio("2560x1708 [SAR 1:1 DAR 640:427]"));
Assert.assertEquals("1:1", MediaInformationParser.parseVideoStreamSampleAspectRatio("3840x4320 [SAR 1:1 DAR 8:9]"));
}
+1 -1
View File
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.android.tools.build:gradle:3.5.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
+2
View File
@@ -9,6 +9,8 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+28 -4
View File
@@ -4,6 +4,20 @@ $(call import-add-path, $(LOCAL_PATH))
MY_ARM_MODE := arm
MY_ARM_NEON := false
MY_PATH := ../app/src/main/cpp
MY_LIBRARY_LTS_SOURCE := $(MY_PATH)/android_lts_support.c
MY_LIBRARY_SOURCE := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
MY_LIBRARY_SHARED_LIBRARIES := libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
MY_LIBRARY_LDLIBS := -llog -lz -landroid
MY_LIBRARY_INCLUDES :=
MY_LIBRARY_WHOLE_STATIC_LIBRARIES :=
# ENABLE FFPLAY
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.ffplay && echo ffplay)","ffplay")
MY_LIBRARY_SOURCE += $(MY_PATH)/fftools_ffplay.c $(MY_PATH)/mobileffplay.c
MY_LIBRARY_WHOLE_STATIC_LIBRARIES += sdl
MY_LIBRARY_LDLIBS += -lGLESv1_CM -lGLESv2
MY_LIBRARY_INCLUDES := -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/sdl/include/SDL2 -I${LOCAL_PATH}/../../src/sdl/src/core/android
endif
# DEFINE ARCH FLAGS
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
@@ -44,11 +58,21 @@ include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := $(MY_ARM_MODE)
LOCAL_MODULE := mobileffmpeg
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include
LOCAL_LDLIBS := -llog -lz -landroid
LOCAL_SHARED_LIBRARIES := c++_shared libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
LOCAL_SRC_FILES := $(MY_LIBRARY_SOURCE)
ifeq ($(TARGET_PLATFORM),android-16)
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
else ifeq ($(TARGET_PLATFORM),android-17)
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
endif
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include ${MY_LIBRARY_INCLUDES}
LOCAL_LDLIBS := ${MY_LIBRARY_LDLIBS}
LOCAL_SHARED_LIBRARIES := ${MY_LIBRARY_SHARED_LIBRARIES}
LOCAL_WHOLE_STATIC_LIBRARIES := ${MY_LIBRARY_WHOLE_STATIC_LIBRARIES}
LOCAL_ARM_NEON := ${MY_ARM_NEON}
include $(BUILD_SHARED_LIBRARY)
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.ffplay && echo ffplay)","ffplay")
$(call import-module, sdl)
endif
$(call import-module, ffmpeg)
+10
View File
@@ -0,0 +1,10 @@
LOCAL_PATH := $(call my-dir)
MY_ARM_MODE := arm
MY_SDL_LIB := ../../../prebuilt/android-$(TARGET_ARCH)/sdl/lib
include $(CLEAR_VARS)
LOCAL_ARM_MODE := $(MY_ARM_MODE)
LOCAL_MODULE := sdl
LOCAL_SRC_FILES := $(MY_SDL_LIB)/libSDL2.a
include $(PREBUILT_STATIC_LIBRARY)
-3
View File
@@ -1,3 +0,0 @@
/build
/.idea/
/local.properties
+8 -12
View File
@@ -9,13 +9,13 @@ android {
keyPassword 'android'
}
}
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
applicationId "com.arthenica.mobileffmpeg.test"
minSdkVersion 24
targetSdkVersion 28
versionCode 240430
versionName "4.3"
targetSdkVersion 29
versionCode 240431
versionName "4.3.1"
}
buildTypes {
debug {
@@ -51,12 +51,8 @@ android.applicationVariants.all { variant ->
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.arthenica:mobile-ffmpeg-full:4.3'
// implementation project(':app')
testImplementation 'junit:junit:4.12'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation project(':app')
// implementation 'com.arthenica:mobile-ffmpeg-full:4.3.1'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}
Binary file not shown.
@@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -1,45 +0,0 @@
/*
* Copyright (c) 2018 Taner Sener
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.test;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.arthenica.mobileffmpeg.test", appContext.getPackageName());
}
}
@@ -20,13 +20,20 @@
android:largeHeap="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.arthenica.mobileffmpeg.test.MainActivity"
android:theme="@style/AppTheme">
<activity android:name="com.arthenica.mobileffmpeg.test.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.arthenica.mobileffmpeg.player.FullScreenActivity"
android:label="play"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
</activity>
</application>
</manifest>
@@ -21,93 +21,81 @@ package com.arthenica.mobileffmpeg.test;
import android.app.AlertDialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFmpeg;
import com.arthenica.mobileffmpeg.LogCallback;
import com.arthenica.mobileffmpeg.LogMessage;
import com.arthenica.mobileffmpeg.util.ExecuteCallback;
import com.arthenica.mobileffmpeg.util.DialogUtil;
import com.arthenica.mobileffmpeg.util.SingleExecuteCallback;
import java.io.File;
import java.util.concurrent.Callable;
import static com.arthenica.mobileffmpeg.FFmpeg.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.test.MainActivity.TAG;
public class AudioTabFragment extends Fragment implements AdapterView.OnItemSelectedListener {
private MainActivity mainActivity;
private AlertDialog progressDialog;
private Button encodeButton;
private TextView outputText;
private String selectedCodec;
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_audio_tab, container, false);
public AudioTabFragment() {
super(R.layout.fragment_audio_tab);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getView() != null) {
Spinner audioCodecSpinner = getView().findViewById(R.id.audioCodecSpinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mainActivity,
R.array.audio_codec, R.layout.spinner_item);
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
audioCodecSpinner.setAdapter(adapter);
audioCodecSpinner.setOnItemSelectedListener(this);
Spinner audioCodecSpinner = view.findViewById(R.id.audioCodecSpinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(requireContext(),
R.array.audio_codec, R.layout.spinner_item);
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
audioCodecSpinner.setAdapter(adapter);
audioCodecSpinner.setOnItemSelectedListener(this);
encodeButton = getView().findViewById(R.id.encodeButton);
encodeButton.setOnClickListener(new View.OnClickListener() {
encodeButton = view.findViewById(R.id.encodeButton);
encodeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
encodeAudio();
// encodeChromaprint();
}
});
encodeButton.setEnabled(false);
@Override
public void onClick(View v) {
encodeAudio();
// encodeChromaprint();
}
});
encodeButton.setEnabled(false);
outputText = getView().findViewById(R.id.outputText);
outputText.setMovementMethod(new ScrollingMovementMethod());
}
outputText = view.findViewById(R.id.outputText);
outputText.setMovementMethod(new ScrollingMovementMethod());
progressDialog = mainActivity.createProgressDialog("Encoding audio");
progressDialog = DialogUtil.createProgressDialog(requireContext(), "Encoding audio");
selectedCodec = getResources().getStringArray(R.array.audio_codec)[0];
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
setActive();
}
public void onResume() {
super.onResume();
setActive();
}
public void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
public static AudioTabFragment newInstance(final MainActivity mainActivity) {
final AudioTabFragment fragment = new AudioTabFragment();
fragment.setMainActivity(mainActivity);
return fragment;
public static AudioTabFragment newInstance() {
return new AudioTabFragment();
}
public void enableLogCallback() {
@@ -159,7 +147,7 @@ public class AudioTabFragment extends Fragment implements AdapterView.OnItemSele
android.util.Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'", ffmpegCommand));
MainActivity.executeAsync(new ExecuteCallback() {
MainActivity.executeAsync(new SingleExecuteCallback() {
@Override
public void apply(final int returnCode, final String commandOutput) {
@@ -172,10 +160,10 @@ public class AudioTabFragment extends Fragment implements AdapterView.OnItemSele
@Override
public Object call() {
if (returnCode == RETURN_CODE_SUCCESS) {
Popup.show(mainActivity, "Encode completed successfully.");
Popup.show(requireContext(), "Encode completed successfully.");
android.util.Log.d(TAG, "Encode completed successfully.");
} else {
Popup.show(mainActivity, "Encode failed. Please check log for the details.");
Popup.show(requireContext(), "Encode failed. Please check log for the details.");
android.util.Log.d(TAG, String.format("Encode failed with rc=%d", returnCode));
}
@@ -218,7 +206,7 @@ public class AudioTabFragment extends Fragment implements AdapterView.OnItemSele
android.util.Log.d(TAG, "AUDIO sample created");
} else {
android.util.Log.d(TAG, String.format("Creating AUDIO sample failed with rc=%d", result));
Popup.show(mainActivity, "Creating AUDIO sample failed. Please check log for the details.");
Popup.show(requireContext(), "Creating AUDIO sample failed. Please check log for the details.");
}
}
@@ -260,11 +248,11 @@ public class AudioTabFragment extends Fragment implements AdapterView.OnItemSele
}
final String audio = "audio." + extension;
return new File(mainActivity.getFilesDir(), audio);
return new File(requireContext().getFilesDir(), audio);
}
public File getAudioSampleFile() {
return new File(mainActivity.getFilesDir(), "audio-sample.wav");
return new File(requireContext().getFilesDir(), "audio-sample.wav");
}
public void setActive() {
@@ -272,7 +260,7 @@ public class AudioTabFragment extends Fragment implements AdapterView.OnItemSele
disableLogCallback();
createAudioSample();
enableLogCallback();
Popup.show(mainActivity, Tooltip.AUDIO_TEST_TOOLTIP_TEXT);
Popup.show(requireContext(), Tooltip.AUDIO_TEST_TOOLTIP_TEXT);
}
public void appendLog(final String logMessage) {
@@ -20,84 +20,72 @@
package com.arthenica.mobileffmpeg.test;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.method.ScrollingMovementMethod;
import android.util.AndroidRuntimeException;
import android.view.LayoutInflater;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFmpeg;
import com.arthenica.mobileffmpeg.FFprobe;
import com.arthenica.mobileffmpeg.LogCallback;
import com.arthenica.mobileffmpeg.LogMessage;
import com.arthenica.mobileffmpeg.util.ExecuteCallback;
import java.util.concurrent.Callable;
public class CommandTabFragment extends Fragment {
private MainActivity mainActivity;
private EditText commandText;
private TextView outputText;
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_command_tab, container, false);
public CommandTabFragment() {
super(R.layout.fragment_command_tab);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getView() != null) {
commandText = getView().findViewById(R.id.commandText);
commandText = view.findViewById(R.id.commandText);
View runButton = getView().findViewById(R.id.runButton);
runButton.setOnClickListener(new View.OnClickListener() {
View runFFmpegButton = view.findViewById(R.id.runFFmpegButton);
runFFmpegButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
runFFmpeg();
}
});
@Override
public void onClick(View v) {
runFFmpeg();
}
});
View runAsyncButton = getView().findViewById(R.id.runAsyncButton);
runAsyncButton.setOnClickListener(new View.OnClickListener() {
View runFFprobeButton = view.findViewById(R.id.runFFprobeButton);
runFFprobeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
runFFmpegAsync();
}
});
@Override
public void onClick(View v) {
runFFprobe();
}
});
outputText = getView().findViewById(R.id.outputText);
outputText.setMovementMethod(new ScrollingMovementMethod());
}
outputText = view.findViewById(R.id.outputText);
outputText.setMovementMethod(new ScrollingMovementMethod());
android.util.Log.d(MainActivity.TAG, "Last command output was: " + FFmpeg.getLastCommandOutput());
Log.d(MainActivity.TAG, "Last command output was: " + Config.getLastCommandOutput());
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
setActive();
}
public void onResume() {
super.onResume();
setActive();
}
public void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
public static CommandTabFragment newInstance(final MainActivity mainActivity) {
final CommandTabFragment fragment = new CommandTabFragment();
fragment.setMainActivity(mainActivity);
return fragment;
public static CommandTabFragment newInstance() {
return new CommandTabFragment();
}
public void enableLogCallback() {
@@ -122,9 +110,9 @@ public class CommandTabFragment extends Fragment {
public void runFFmpeg() {
clearLog();
final String ffmpegCommand = String.format("-hide_banner %s", commandText.getText().toString());
final String ffmpegCommand = String.format("%s", commandText.getText().toString());
android.util.Log.d(MainActivity.TAG, "Testing COMMAND synchronously.");
android.util.Log.d(MainActivity.TAG, "Testing FFmpeg COMMAND synchronously.");
android.util.Log.d(MainActivity.TAG, String.format("FFmpeg process started with arguments\n\'%s\'", ffmpegCommand));
@@ -133,44 +121,32 @@ public class CommandTabFragment extends Fragment {
android.util.Log.d(MainActivity.TAG, String.format("FFmpeg process exited with rc %d", result));
if (result != 0) {
Popup.show(mainActivity, "Command failed. Please check output for the details.");
Popup.show(requireContext(), "Command failed. Please check output for the details.");
}
}
public void runFFmpegAsync() {
public void runFFprobe() {
clearLog();
final String ffmpegCommand = String.format("-hide_banner %s", commandText.getText().toString());
final String ffprobeCommand = String.format("%s", commandText.getText().toString());
android.util.Log.d(MainActivity.TAG, "Testing COMMAND asynchronously.");
android.util.Log.d(MainActivity.TAG, "Testing FFprobe COMMAND synchronously.");
android.util.Log.d(MainActivity.TAG, String.format("FFmpeg process started with arguments\n\'%s\'", ffmpegCommand));
android.util.Log.d(MainActivity.TAG, String.format("FFprobe process started with arguments\n\'%s\'", ffprobeCommand));
MainActivity.executeAsync(new ExecuteCallback() {
int result = FFprobe.execute(ffprobeCommand);
@Override
public void apply(int result, final String commandOutput) {
android.util.Log.d(MainActivity.TAG, String.format("FFprobe process exited with rc %d", result));
android.util.Log.d(MainActivity.TAG, String.format("FFmpeg process exited with rc %d", result));
if (result != 0) {
MainActivity.addUIAction(new Callable() {
@Override
public Object call() {
Popup.show(mainActivity, "Command failed. Please check output for the details.");
return null;
}
});
}
}
}, ffmpegCommand);
if (result != 0) {
Popup.show(requireContext(), "Command failed. Please check output for the details.");
}
}
public void setActive() {
android.util.Log.i(MainActivity.TAG, "Command Tab Activated");
private void setActive() {
Log.i(MainActivity.TAG, "Command Tab Activated");
enableLogCallback();
Popup.show(mainActivity, Tooltip.COMMAND_TEST_TOOLTIP_TEXT);
Popup.show(requireContext(), Tooltip.COMMAND_TEST_TOOLTIP_TEXT);
}
public void appendLog(final String logMessage) {
@@ -20,18 +20,18 @@
package com.arthenica.mobileffmpeg.test;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFmpeg;
import com.arthenica.mobileffmpeg.FFprobe;
import com.arthenica.mobileffmpeg.LogCallback;
import com.arthenica.mobileffmpeg.LogMessage;
import com.arthenica.mobileffmpeg.MediaInformation;
@@ -45,52 +45,40 @@ public class HttpsTabFragment extends Fragment {
public static final String HTTPS_TEST_DEFAULT_URL = "https://download.blender.org/peach/trailer/trailer_1080p.ogg";
private MainActivity mainActivity;
private EditText urlText;
private TextView outputText;
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_https_tab, container, false);
public HttpsTabFragment() {
super(R.layout.fragment_https_tab);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getView() != null) {
urlText = getView().findViewById(R.id.urlText);
urlText = view.findViewById(R.id.urlText);
View getInfoButton = getView().findViewById(R.id.getInfoButton);
getInfoButton.setOnClickListener(new View.OnClickListener() {
View getInfoButton = view.findViewById(R.id.getInfoButton);
getInfoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getInfo();
}
});
@Override
public void onClick(View v) {
getInfo();
}
});
outputText = getView().findViewById(R.id.outputText);
outputText.setMovementMethod(new ScrollingMovementMethod());
}
outputText = view.findViewById(R.id.outputText);
outputText.setMovementMethod(new ScrollingMovementMethod());
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
setActive();
}
public void onResume() {
super.onResume();
setActive();
}
public void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
public static HttpsTabFragment newInstance(final MainActivity mainActivity) {
final HttpsTabFragment fragment = new HttpsTabFragment();
fragment.setMainActivity(mainActivity);
return fragment;
public static HttpsTabFragment newInstance() {
return new HttpsTabFragment();
}
public void enableLogCallback() {
@@ -123,7 +111,7 @@ public class HttpsTabFragment extends Fragment {
}
// HTTPS COMMAND ARGUMENTS
MediaInformation information = FFmpeg.getMediaInformation(testUrl);
MediaInformation information = FFprobe.getMediaInformation(testUrl);
if (information == null) {
appendLog("Get media information failed\n");
} else {
@@ -220,9 +208,9 @@ public class HttpsTabFragment extends Fragment {
}
public void setActive() {
android.util.Log.i(MainActivity.TAG, "Https Tab Activated");
Log.i(MainActivity.TAG, "Https Tab Activated");
enableLogCallback();
Popup.show(mainActivity, Tooltip.HTTPS_TEST_TOOLTIP_TEXT);
Popup.show(requireContext(), Tooltip.HTTPS_TEST_TOOLTIP_TEXT);
}
public void appendLog(final String logMessage) {
@@ -20,34 +20,25 @@
package com.arthenica.mobileffmpeg.test;
import android.Manifest;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.viewpager.widget.PagerTabStrip;
import androidx.viewpager.widget.ViewPager;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.util.AsyncExecuteTask;
import com.arthenica.mobileffmpeg.util.ExecuteCallback;
import com.arthenica.mobileffmpeg.util.AsyncSingleFFmpegExecuteTask;
import com.arthenica.mobileffmpeg.util.ResourcesUtil;
import com.arthenica.mobileffmpeg.util.SingleExecuteCallback;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Queue;
@@ -96,12 +87,6 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
android.support.v7.app.ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
supportActionBar.setCustomView(R.layout.action_bar);
}
PagerTabStrip pagerTabStrip = findViewById(R.id.pagerTabStrip);
if (pagerTabStrip != null) {
pagerTabStrip.setDrawFullUnderline(false);
@@ -110,7 +95,7 @@ public class MainActivity extends AppCompatActivity {
}
final ViewPager viewPager = findViewById(R.id.pager);
viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager(), this, 7));
viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager(), this));
waitForUIAction();
@@ -153,14 +138,14 @@ public class MainActivity extends AppCompatActivity {
}
/**
* <p>Starts a new asynchronous FFmpeg operation with arguments provided.
* <p>Starts a new asynchronous FFmpeg operation with command provided.
*
* @param executeCallback callback function to receive result of this execution
* @param arguments FFmpeg command options/arguments
* @param singleExecuteCallback callback function to receive result of this execution
* @param command FFmpeg command
*/
public static void executeAsync(final ExecuteCallback executeCallback, final String arguments) {
final AsyncExecuteTask asyncCommandTask = new AsyncExecuteTask(executeCallback);
asyncCommandTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, arguments);
public static void executeAsync(final SingleExecuteCallback singleExecuteCallback, final String command) {
final AsyncSingleFFmpegExecuteTask asyncCommandTask = new AsyncSingleFFmpegExecuteTask(command, singleExecuteCallback);
asyncCommandTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static void waitForUIAction() {
@@ -171,84 +156,12 @@ public class MainActivity extends AppCompatActivity {
actionQueue.add(callable);
}
public AlertDialog createProgressDialog(final String text) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (inflater != null) {
View dialogView = inflater.inflate(R.layout.progress_dialog_layout, null);
builder.setView(dialogView);
TextView textView = dialogView.findViewById(R.id.progressDialogText);
if (textView != null) {
textView.setText(text);
}
}
builder.setCancelable(false);
return builder.create();
}
public AlertDialog createCancellableProgressDialog(final String text, final View.OnClickListener onClickListener) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (inflater != null) {
View dialogView = inflater.inflate(R.layout.cancellable_progress_dialog_layout, null);
builder.setView(dialogView);
TextView textView = dialogView.findViewById(R.id.progressDialogText);
if (textView != null) {
textView.setText(text);
}
Button cancelButton = dialogView.findViewById(R.id.cancelButton);
if (cancelButton != null) {
cancelButton.setOnClickListener(onClickListener);
}
}
builder.setCancelable(false);
return builder.create();
}
protected void resourceToFile(final int resourceId, final File file) throws IOException {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resourceId);
if (file.exists()) {
file.delete();
}
FileOutputStream outputStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.flush();
outputStream.close();
}
protected void rawResourceToFile(final int resourceId, final File file) throws IOException {
final InputStream inputStream = getResources().openRawResource(resourceId);
if (file.exists()) {
file.delete();
}
final FileOutputStream outputStream = new FileOutputStream(file);
try {
final byte[] buffer = new byte[1024];
int readSize;
while ((readSize = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, readSize);
}
} catch (final IOException e) {
Log.e(TAG, "Saving raw resource failed.", e);
} finally {
inputStream.close();
outputStream.flush();
outputStream.close();
}
}
protected void registerAppFont() throws IOException {
final File cacheDirectory = getCacheDir();
// SAVE FONTS
rawResourceToFile(R.raw.doppioone_regular, new File(cacheDirectory, "doppioone_regular.ttf"));
rawResourceToFile(R.raw.truenorg, new File(cacheDirectory, "truenorg.otf"));
ResourcesUtil.rawResourceToFile(getResources(), R.raw.doppioone_regular, new File(cacheDirectory, "doppioone_regular.ttf"));
ResourcesUtil.rawResourceToFile(getResources(), R.raw.truenorg, new File(cacheDirectory, "truenorg.otf"));
final HashMap<String, String> fontNameMapping = new HashMap<>();
fontNameMapping.put("MyFontName", "Doppio One");
@@ -19,44 +19,45 @@
package com.arthenica.mobileffmpeg.test;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
public class PagerAdapter extends FragmentPagerAdapter {
private final MainActivity mainActivity;
private int numberOfTabs;
private static final int NUMBER_OF_TABS = 7;
PagerAdapter(final FragmentManager fragmentManager, final MainActivity mainActivity, final int numberOfTabs) {
super(fragmentManager);
private final Context context;
this.mainActivity = mainActivity;
this.numberOfTabs = numberOfTabs;
PagerAdapter(FragmentManager fragmentManager, Context context) {
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.context = context;
}
@Override
public Fragment getItem(final int position) {
switch (position) {
case 0: {
return CommandTabFragment.newInstance(mainActivity);
return CommandTabFragment.newInstance();
}
case 1: {
return VideoTabFragment.newInstance(mainActivity);
return VideoTabFragment.newInstance();
}
case 2: {
return HttpsTabFragment.newInstance(mainActivity);
return HttpsTabFragment.newInstance();
}
case 3: {
return AudioTabFragment.newInstance(mainActivity);
return AudioTabFragment.newInstance();
}
case 4: {
return SubtitleTabFragment.newInstance(mainActivity);
return SubtitleTabFragment.newInstance();
}
case 5: {
return VidStabTabFragment.newInstance(mainActivity);
return VidStabTabFragment.newInstance();
}
case 6: {
return PipeTabFragment.newInstance(mainActivity);
return PipeTabFragment.newInstance();
}
default: {
return null;
@@ -66,32 +67,32 @@ public class PagerAdapter extends FragmentPagerAdapter {
@Override
public int getCount() {
return numberOfTabs;
return NUMBER_OF_TABS;
}
@Override
public CharSequence getPageTitle(final int position) {
switch (position) {
case 0: {
return mainActivity.getString(R.string.command_tab);
return context.getString(R.string.command_tab);
}
case 1: {
return mainActivity.getString(R.string.video_tab);
return context.getString(R.string.video_tab);
}
case 2: {
return mainActivity.getString(R.string.https_tab);
return context.getString(R.string.https_tab);
}
case 3: {
return mainActivity.getString(R.string.audio_tab);
return context.getString(R.string.audio_tab);
}
case 4: {
return mainActivity.getString(R.string.subtitle_tab);
return context.getString(R.string.subtitle_tab);
}
case 5: {
return mainActivity.getString(R.string.vidstab_tab);
return context.getString(R.string.vidstab_tab);
}
case 6: {
return mainActivity.getString(R.string.pipe_tab);
return context.getString(R.string.pipe_tab);
}
default: {
return null;
@@ -24,83 +24,72 @@ import android.media.MediaPlayer;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.MediaController;
import android.widget.TextView;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.LogCallback;
import com.arthenica.mobileffmpeg.LogMessage;
import com.arthenica.mobileffmpeg.Statistics;
import com.arthenica.mobileffmpeg.StatisticsCallback;
import com.arthenica.mobileffmpeg.util.AsyncCatImageTask;
import com.arthenica.mobileffmpeg.util.ExecuteCallback;
import com.arthenica.mobileffmpeg.util.DialogUtil;
import com.arthenica.mobileffmpeg.util.ResourcesUtil;
import com.arthenica.mobileffmpeg.util.SingleExecuteCallback;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.concurrent.Callable;
import static com.arthenica.mobileffmpeg.FFmpeg.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.test.MainActivity.TAG;
public class PipeTabFragment extends Fragment {
private MainActivity mainActivity;
private VideoView videoView;
private AlertDialog progressDialog;
private Statistics statistics;
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_pipe_tab, container, false);
public PipeTabFragment() {
super(R.layout.fragment_pipe_tab);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getView() != null) {
View createButton = getView().findViewById(R.id.createButton);
if (createButton != null) {
createButton.setOnClickListener(new View.OnClickListener() {
View createButton = view.findViewById(R.id.createButton);
if (createButton != null) {
createButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createVideo();
}
});
}
videoView = getView().findViewById(R.id.videoPlayerFrame);
@Override
public void onClick(View v) {
createVideo();
}
});
}
progressDialog = mainActivity.createProgressDialog("Creating video");
videoView = view.findViewById(R.id.videoPlayerFrame);
progressDialog = DialogUtil.createProgressDialog(requireContext(), "Creating video");
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
setActive();
}
public void onResume() {
super.onResume();
setActive();
}
public void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
public static PipeTabFragment newInstance(final MainActivity mainActivity) {
final PipeTabFragment fragment = new PipeTabFragment();
fragment.setMainActivity(mainActivity);
return fragment;
public static PipeTabFragment newInstance() {
return new PipeTabFragment();
}
public void enableLogCallback() {
@@ -137,14 +126,14 @@ public class PipeTabFragment extends Fragment {
}
public void createVideo() {
final File image1File = new File(mainActivity.getCacheDir(), "colosseum.jpg");
final File image2File = new File(mainActivity.getCacheDir(), "pyramid.jpg");
final File image3File = new File(mainActivity.getCacheDir(), "tajmahal.jpg");
final File image1File = new File(requireContext().getCacheDir(), "colosseum.jpg");
final File image2File = new File(requireContext().getCacheDir(), "pyramid.jpg");
final File image3File = new File(requireContext().getCacheDir(), "tajmahal.jpg");
final File videoFile = getVideoFile();
String pipe1 = Config.registerNewFFmpegPipe(mainActivity);
String pipe2 = Config.registerNewFFmpegPipe(mainActivity);
String pipe3 = Config.registerNewFFmpegPipe(mainActivity);
String pipe1 = Config.registerNewFFmpegPipe(requireContext());
String pipe2 = Config.registerNewFFmpegPipe(requireContext());
String pipe3 = Config.registerNewFFmpegPipe(requireContext());
try {
@@ -159,15 +148,15 @@ public class PipeTabFragment extends Fragment {
showProgressDialog();
mainActivity.resourceToFile(R.drawable.colosseum, image1File);
mainActivity.resourceToFile(R.drawable.pyramid, image2File);
mainActivity.resourceToFile(R.drawable.tajmahal, image3File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.colosseum, image1File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.pyramid, image2File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.tajmahal, image3File);
final String ffmpegCommand = Video.generateCreateVideoWithPipesScript(pipe1, pipe2, pipe3, videoFile.getAbsolutePath());
Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'", ffmpegCommand));
MainActivity.executeAsync(new ExecuteCallback() {
MainActivity.executeAsync(new SingleExecuteCallback() {
@Override
public void apply(final int returnCode, final String commandOutput) {
@@ -183,7 +172,7 @@ public class PipeTabFragment extends Fragment {
Log.d(TAG, "Create completed successfully; playing video.");
playVideo();
} else {
Popup.show(mainActivity, "Create failed. Please check log for the details.");
Popup.show(requireContext(), "Create failed. Please check log for the details.");
Log.d(TAG, String.format("Create failed with rc=%d", returnCode));
}
@@ -200,12 +189,12 @@ public class PipeTabFragment extends Fragment {
} catch (IOException e) {
Log.e(TAG, "Create video failed", e);
Popup.show(mainActivity, "Create video failed");
Popup.show(requireContext(), "Create video failed");
}
}
protected void playVideo() {
MediaController mediaController = new MediaController(mainActivity);
MediaController mediaController = new MediaController(requireContext());
mediaController.setAnchorView(videoView);
videoView.setVideoURI(Uri.parse("file://" + getVideoFile().getAbsolutePath()));
videoView.setMediaController(mediaController);
@@ -229,14 +218,14 @@ public class PipeTabFragment extends Fragment {
}
protected File getVideoFile() {
return new File(mainActivity.getFilesDir(), "video.mp4");
return new File(requireContext().getFilesDir(), "video.mp4");
}
public void setActive() {
Log.i(MainActivity.TAG, "Pipe Tab Activated");
enableLogCallback();
enableStatisticsCallback();
Popup.show(mainActivity, Tooltip.PIPE_TEST_TOOLTIP_TEXT);
Popup.show(requireContext(), Tooltip.PIPE_TEST_TOOLTIP_TEXT);
}
protected void showProgressDialog() {
@@ -273,7 +262,7 @@ public class PipeTabFragment extends Fragment {
@Override
public Object call() {
PipeTabFragment.this.progressDialog = mainActivity.createProgressDialog("Creating video");
PipeTabFragment.this.progressDialog = DialogUtil.createProgressDialog(requireContext(), "Creating video");
return null;
}
});
@@ -23,33 +23,33 @@ import android.app.AlertDialog;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.MediaController;
import android.widget.TextView;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFmpeg;
import com.arthenica.mobileffmpeg.LogCallback;
import com.arthenica.mobileffmpeg.LogMessage;
import com.arthenica.mobileffmpeg.Statistics;
import com.arthenica.mobileffmpeg.StatisticsCallback;
import com.arthenica.mobileffmpeg.util.ExecuteCallback;
import com.arthenica.mobileffmpeg.util.DialogUtil;
import com.arthenica.mobileffmpeg.util.ResourcesUtil;
import com.arthenica.mobileffmpeg.util.SingleExecuteCallback;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.concurrent.Callable;
import static com.arthenica.mobileffmpeg.FFmpeg.RETURN_CODE_CANCEL;
import static com.arthenica.mobileffmpeg.FFmpeg.RETURN_CODE_MULTIPLE_EXECUTIONS_NOT_ALLOWED;
import static com.arthenica.mobileffmpeg.FFmpeg.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.Config.RETURN_CODE_CANCEL;
import static com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.test.MainActivity.TAG;
public class SubtitleTabFragment extends Fragment {
@@ -60,54 +60,42 @@ public class SubtitleTabFragment extends Fragment {
BURNING
}
private MainActivity mainActivity;
private VideoView videoView;
private AlertDialog createProgressDialog;
private AlertDialog burnProgressDialog;
private Statistics statistics;
private State state;
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_subtitle_tab, container, false);
public SubtitleTabFragment() {
super(R.layout.fragment_subtitle_tab);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getView() != null) {
View burnSubtitlesButton = getView().findViewById(R.id.burnSubtitlesButton);
burnSubtitlesButton.setOnClickListener(new View.OnClickListener() {
View burnSubtitlesButton = view.findViewById(R.id.burnSubtitlesButton);
burnSubtitlesButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
burnSubtitles();
}
});
@Override
public void onClick(View v) {
burnSubtitles();
}
});
videoView = getView().findViewById(R.id.videoPlayerFrame);
}
videoView = view.findViewById(R.id.videoPlayerFrame);
state = State.IDLE;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
setActive();
}
public void onResume() {
super.onResume();
setActive();
}
public void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
public static SubtitleTabFragment newInstance(final MainActivity mainActivity) {
final SubtitleTabFragment fragment = new SubtitleTabFragment();
fragment.setMainActivity(mainActivity);
return fragment;
public static SubtitleTabFragment newInstance() {
return new SubtitleTabFragment();
}
public void enableLogCallback() {
@@ -139,9 +127,9 @@ public class SubtitleTabFragment extends Fragment {
}
public void burnSubtitles() {
final File image1File = new File(mainActivity.getCacheDir(), "colosseum.jpg");
final File image2File = new File(mainActivity.getCacheDir(), "pyramid.jpg");
final File image3File = new File(mainActivity.getCacheDir(), "tajmahal.jpg");
final File image1File = new File(requireContext().getCacheDir(), "colosseum.jpg");
final File image2File = new File(requireContext().getCacheDir(), "pyramid.jpg");
final File image3File = new File(requireContext().getCacheDir(), "tajmahal.jpg");
final File videoFile = getVideoFile();
final File videoWithSubtitlesFile = getVideoWithSubtitlesFile();
@@ -162,10 +150,10 @@ public class SubtitleTabFragment extends Fragment {
showCreateProgressDialog();
mainActivity.resourceToFile(R.drawable.colosseum, image1File);
mainActivity.resourceToFile(R.drawable.pyramid, image2File);
mainActivity.resourceToFile(R.drawable.tajmahal, image3File);
mainActivity.rawResourceToFile(R.raw.subtitle, getSubtitleFile());
ResourcesUtil.resourceToFile(getResources(), R.drawable.colosseum, image1File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.pyramid, image2File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.tajmahal, image3File);
ResourcesUtil.rawResourceToFile(getResources(), R.raw.subtitle, getSubtitleFile());
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath(), "mpeg4", "");
@@ -173,16 +161,12 @@ public class SubtitleTabFragment extends Fragment {
state = State.CREATING;
MainActivity.executeAsync(new ExecuteCallback() {
MainActivity.executeAsync(new SingleExecuteCallback() {
@Override
public void apply(final int returnCode, final String commandOutput) {
Log.d(TAG, String.format("FFmpeg process exited with rc %d", returnCode));
if (returnCode != RETURN_CODE_MULTIPLE_EXECUTIONS_NOT_ALLOWED) {
state = State.IDLE;
}
hideCreateProgressDialog();
MainActivity.addUIAction(new Callable() {
@@ -201,16 +185,12 @@ public class SubtitleTabFragment extends Fragment {
state = State.BURNING;
MainActivity.executeAsync(new ExecuteCallback() {
MainActivity.executeAsync(new SingleExecuteCallback() {
@Override
public void apply(final int returnCode, final String commandOutput) {
Log.d(TAG, String.format("FFmpeg process exited with rc %d", returnCode));
if (returnCode != RETURN_CODE_MULTIPLE_EXECUTIONS_NOT_ALLOWED) {
state = State.IDLE;
}
hideBurnProgressDialog();
MainActivity.addUIAction(new Callable() {
@@ -221,13 +201,10 @@ public class SubtitleTabFragment extends Fragment {
Log.d(TAG, "Burn subtitles completed successfully; playing video.");
playVideo();
} else if (returnCode == RETURN_CODE_CANCEL) {
Popup.show(mainActivity, "Burn subtitles operation cancelled.");
Popup.show(requireContext(), "Burn subtitles operation cancelled.");
Log.e(TAG, "Burn subtitles operation cancelled");
} else if (returnCode == RETURN_CODE_MULTIPLE_EXECUTIONS_NOT_ALLOWED) {
Popup.show(mainActivity, "Multiple burn subtitles operations not allowed.");
Log.e(TAG, "Multiple burn subtitles operations not allowed");
} else {
Popup.show(mainActivity, "Burn subtitles failed. Please check log for the details.");
Popup.show(requireContext(), "Burn subtitles failed. Please check log for the details.");
Log.e(TAG, String.format("Burn subtitles failed with rc=%d", returnCode));
}
@@ -238,13 +215,10 @@ public class SubtitleTabFragment extends Fragment {
}, burnSubtitlesCommand);
} else if (returnCode == RETURN_CODE_CANCEL) {
Popup.show(mainActivity, "Create operation cancelled.");
Popup.show(requireContext(), "Create operation cancelled.");
Log.e(TAG, "Create operation cancelled");
} else if (returnCode == RETURN_CODE_MULTIPLE_EXECUTIONS_NOT_ALLOWED) {
Popup.show(mainActivity, "Multiple create operations not allowed.");
Log.e(TAG, "Multiple create operations not allowed");
} else {
Popup.show(mainActivity, "Create video failed. Please check log for the details.");
Popup.show(requireContext(), "Create video failed. Please check log for the details.");
Log.e(TAG, String.format("Create failed with rc=%d", returnCode));
}
@@ -256,12 +230,12 @@ public class SubtitleTabFragment extends Fragment {
} catch (IOException e) {
Log.e(TAG, "Burn subtitles failed", e);
Popup.show(mainActivity, "Burn subtitles failed");
Popup.show(requireContext(), "Burn subtitles failed");
}
}
protected void playVideo() {
MediaController mediaController = new MediaController(mainActivity);
MediaController mediaController = new MediaController(requireContext());
mediaController.setAnchorView(videoView);
videoView.setVideoURI(Uri.parse("file://" + getVideoWithSubtitlesFile().getAbsolutePath()));
videoView.setMediaController(mediaController);
@@ -285,22 +259,22 @@ public class SubtitleTabFragment extends Fragment {
}
public File getSubtitleFile() {
return new File(mainActivity.getCacheDir(), "subtitle.srt");
return new File(requireContext().getCacheDir(), "subtitle.srt");
}
public File getVideoFile() {
return new File(mainActivity.getFilesDir(), "video.mp4");
return new File(requireContext().getFilesDir(), "video.mp4");
}
public File getVideoWithSubtitlesFile() {
return new File(mainActivity.getFilesDir(), "video-with-subtitles.mp4");
return new File(requireContext().getFilesDir(), "video-with-subtitles.mp4");
}
public void setActive() {
android.util.Log.i(MainActivity.TAG, "Subtitle Tab Activated");
Log.i(MainActivity.TAG, "Subtitle Tab Activated");
enableLogCallback();
enableStatisticsCallback();
Popup.show(mainActivity, Tooltip.SUBTITLE_TEST_TOOLTIP_TEXT);
Popup.show(requireContext(), Tooltip.SUBTITLE_TEST_TOOLTIP_TEXT);
}
protected void showCreateProgressDialog() {
@@ -309,7 +283,7 @@ public class SubtitleTabFragment extends Fragment {
statistics = null;
Config.resetStatistics();
createProgressDialog = mainActivity.createCancellableProgressDialog("Creating video", new View.OnClickListener() {
createProgressDialog = DialogUtil.createCancellableProgressDialog(requireContext(), "Creating video", new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -353,7 +327,7 @@ public class SubtitleTabFragment extends Fragment {
statistics = null;
Config.resetStatistics();
burnProgressDialog = mainActivity.createCancellableProgressDialog("Burning subtitles", new View.OnClickListener() {
burnProgressDialog = DialogUtil.createCancellableProgressDialog(requireContext(), "Burning subtitles", new View.OnClickListener() {
@Override
public void onClick(View v) {
FFmpeg.cancel();
@@ -23,78 +23,68 @@ import android.app.AlertDialog;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.MediaController;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.LogCallback;
import com.arthenica.mobileffmpeg.LogMessage;
import com.arthenica.mobileffmpeg.util.ExecuteCallback;
import com.arthenica.mobileffmpeg.util.DialogUtil;
import com.arthenica.mobileffmpeg.util.ResourcesUtil;
import com.arthenica.mobileffmpeg.util.SingleExecuteCallback;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.Callable;
import static com.arthenica.mobileffmpeg.FFmpeg.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.test.MainActivity.TAG;
public class VidStabTabFragment extends Fragment {
private MainActivity mainActivity;
private VideoView videoView;
private VideoView stabilizedVideoView;
private AlertDialog createProgressDialog;
private AlertDialog stabilizeProgressDialog;
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_vidstab_tab, container, false);
public VidStabTabFragment() {
super(R.layout.fragment_vidstab_tab);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getView() != null) {
View stabilizeVideoButton = getView().findViewById(R.id.stabilizeVideoButton);
stabilizeVideoButton.setOnClickListener(new View.OnClickListener() {
View stabilizeVideoButton = view.findViewById(R.id.stabilizeVideoButton);
stabilizeVideoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stabilizeVideo();
}
});
@Override
public void onClick(View v) {
stabilizeVideo();
}
});
videoView = getView().findViewById(R.id.videoPlayerFrame);
stabilizedVideoView = getView().findViewById(R.id.stabilizedVideoPlayerFrame);
}
videoView = view.findViewById(R.id.videoPlayerFrame);
stabilizedVideoView = view.findViewById(R.id.stabilizedVideoPlayerFrame);
createProgressDialog = mainActivity.createProgressDialog("Creating video");
stabilizeProgressDialog = mainActivity.createProgressDialog("Stabilizing video");
createProgressDialog = DialogUtil.createProgressDialog(requireContext(), "Creating video");
stabilizeProgressDialog = DialogUtil.createProgressDialog(requireContext(), "Stabilizing video");
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
setActive();
}
public void onResume() {
super.onResume();
setActive();
}
public void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
public static VidStabTabFragment newInstance(final MainActivity mainActivity) {
final VidStabTabFragment fragment = new VidStabTabFragment();
fragment.setMainActivity(mainActivity);
return fragment;
public static VidStabTabFragment newInstance() {
return new VidStabTabFragment();
}
public void enableLogCallback() {
@@ -108,9 +98,9 @@ public class VidStabTabFragment extends Fragment {
}
public void stabilizeVideo() {
final File image1File = new File(mainActivity.getCacheDir(), "colosseum.jpg");
final File image2File = new File(mainActivity.getCacheDir(), "pyramid.jpg");
final File image3File = new File(mainActivity.getCacheDir(), "tajmahal.jpg");
final File image1File = new File(requireContext().getCacheDir(), "colosseum.jpg");
final File image2File = new File(requireContext().getCacheDir(), "pyramid.jpg");
final File image3File = new File(requireContext().getCacheDir(), "tajmahal.jpg");
final File shakeResultsFile = getShakeResultsFile();
final File videoFile = getVideoFile();
final File stabilizedVideoFile = getStabilizedVideoFile();
@@ -135,15 +125,15 @@ public class VidStabTabFragment extends Fragment {
showCreateProgressDialog();
mainActivity.resourceToFile(R.drawable.colosseum, image1File);
mainActivity.resourceToFile(R.drawable.pyramid, image2File);
mainActivity.resourceToFile(R.drawable.tajmahal, image3File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.colosseum, image1File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.pyramid, image2File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.tajmahal, image3File);
final String ffmpegCommand = Video.generateShakingVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath());
android.util.Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'", ffmpegCommand));
MainActivity.executeAsync(new ExecuteCallback() {
MainActivity.executeAsync(new SingleExecuteCallback() {
@Override
public void apply(final int returnCode, final String commandOutput) {
@@ -165,7 +155,7 @@ public class VidStabTabFragment extends Fragment {
android.util.Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'", analyzeVideoCommand));
MainActivity.executeAsync(new ExecuteCallback() {
MainActivity.executeAsync(new SingleExecuteCallback() {
@Override
public void apply(final int returnCode, final String commandOutput) {
@@ -176,7 +166,7 @@ public class VidStabTabFragment extends Fragment {
android.util.Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'", stabilizeVideoCommand));
MainActivity.executeAsync(new ExecuteCallback() {
MainActivity.executeAsync(new SingleExecuteCallback() {
@Override
public void apply(final int returnCode, final String commandOutput) {
@@ -193,7 +183,7 @@ public class VidStabTabFragment extends Fragment {
playVideo();
playStabilizedVideo();
} else {
Popup.show(mainActivity, "Stabilize video failed. Please check log for the details.");
Popup.show(requireContext(), "Stabilize video failed. Please check log for the details.");
android.util.Log.d(TAG, String.format("Stabilize video failed with rc=%d", returnCode));
}
@@ -205,14 +195,14 @@ public class VidStabTabFragment extends Fragment {
} else {
hideStabilizeProgressDialog();
Popup.show(mainActivity, "Stabilize video failed. Please check log for the details.");
Popup.show(requireContext(), "Stabilize video failed. Please check log for the details.");
android.util.Log.d(TAG, String.format("Stabilize video failed with rc=%d", returnCode));
}
}
}, analyzeVideoCommand);
} else {
Popup.show(mainActivity, "Create video failed. Please check log for the details.");
Popup.show(requireContext(), "Create video failed. Please check log for the details.");
android.util.Log.d(TAG, String.format("Create failed with rc=%d", returnCode));
}
@@ -224,12 +214,12 @@ public class VidStabTabFragment extends Fragment {
} catch (IOException e) {
android.util.Log.e(TAG, "Stabilize video failed", e);
Popup.show(mainActivity, "Stabilize video failed");
Popup.show(requireContext(), "Stabilize video failed");
}
}
protected void playVideo() {
MediaController mediaController = new MediaController(mainActivity);
MediaController mediaController = new MediaController(requireContext());
mediaController.setAnchorView(videoView);
videoView.setVideoURI(Uri.parse("file://" + getVideoFile().getAbsolutePath()));
videoView.setMediaController(mediaController);
@@ -253,7 +243,7 @@ public class VidStabTabFragment extends Fragment {
}
protected void playStabilizedVideo() {
MediaController mediaController = new MediaController(mainActivity);
MediaController mediaController = new MediaController(requireContext());
mediaController.setAnchorView(stabilizedVideoView);
stabilizedVideoView.setVideoURI(Uri.parse("file://" + getStabilizedVideoFile().getAbsolutePath()));
stabilizedVideoView.setMediaController(mediaController);
@@ -277,21 +267,21 @@ public class VidStabTabFragment extends Fragment {
}
public File getShakeResultsFile() {
return new File(mainActivity.getCacheDir(), "transforms.trf");
return new File(requireContext().getCacheDir(), "transforms.trf");
}
public File getVideoFile() {
return new File(mainActivity.getFilesDir(), "video.mp4");
return new File(requireContext().getFilesDir(), "video.mp4");
}
public File getStabilizedVideoFile() {
return new File(mainActivity.getFilesDir(), "video-stabilized.mp4");
return new File(requireContext().getFilesDir(), "video-stabilized.mp4");
}
public void setActive() {
android.util.Log.i(MainActivity.TAG, "VidStab Tab Activated");
Log.i(MainActivity.TAG, "VidStab Tab Activated");
enableLogCallback();
Popup.show(mainActivity, Tooltip.VIDSTAB_TEST_TOOLTIP_TEXT);
Popup.show(requireContext(), Tooltip.VIDSTAB_TEST_TOOLTIP_TEXT);
}
protected void showCreateProgressDialog() {
@@ -20,101 +20,90 @@
package com.arthenica.mobileffmpeg.test;
import android.app.AlertDialog;
import android.media.MediaPlayer;
import android.net.Uri;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.AndroidRuntimeException;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.MediaController;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.arthenica.mobileffmpeg.Config;
import com.arthenica.mobileffmpeg.FFmpeg;
import com.arthenica.mobileffmpeg.FFplay;
import com.arthenica.mobileffmpeg.LogCallback;
import com.arthenica.mobileffmpeg.LogMessage;
import com.arthenica.mobileffmpeg.Statistics;
import com.arthenica.mobileffmpeg.StatisticsCallback;
import com.arthenica.mobileffmpeg.util.ExecuteCallback;
import com.arthenica.mobileffmpeg.player.GenericMotionListener;
import com.arthenica.mobileffmpeg.player.PlayerSession;
import com.arthenica.mobileffmpeg.player.PlayerSurface;
import com.arthenica.mobileffmpeg.util.DialogUtil;
import com.arthenica.mobileffmpeg.util.ResourcesUtil;
import com.arthenica.mobileffmpeg.util.SingleExecuteCallback;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.concurrent.Callable;
import static com.arthenica.mobileffmpeg.FFmpeg.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS;
import static com.arthenica.mobileffmpeg.test.MainActivity.TAG;
public class VideoTabFragment extends Fragment implements AdapterView.OnItemSelectedListener {
private MainActivity mainActivity;
private VideoView videoView;
private AlertDialog progressDialog;
private String selectedCodec;
private Statistics statistics;
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_video_tab, container, false);
private PlayerSession playerSession;
private PlayerSurface playerSurface;
public VideoTabFragment() {
super(R.layout.fragment_video_tab);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getView() != null) {
Spinner videoCodecSpinner = getView().findViewById(R.id.videoCodecSpinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mainActivity,
R.array.video_codec, R.layout.spinner_item);
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
videoCodecSpinner.setAdapter(adapter);
videoCodecSpinner.setOnItemSelectedListener(this);
Spinner videoCodecSpinner = view.findViewById(R.id.videoCodecSpinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(requireContext(),
R.array.video_codec, R.layout.spinner_item);
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
videoCodecSpinner.setAdapter(adapter);
videoCodecSpinner.setOnItemSelectedListener(this);
View encodeButton = getView().findViewById(R.id.encodeButton);
if (encodeButton != null) {
encodeButton.setOnClickListener(new View.OnClickListener() {
View encodeButton = view.findViewById(R.id.encodeButton);
if (encodeButton != null) {
encodeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
encodeVideo();
// encodeWebp();
}
});
}
videoView = getView().findViewById(R.id.videoPlayerFrame);
@Override
public void onClick(View v) {
playVideo();
// encodeVideo();
// encodeWebp();
}
});
}
progressDialog = mainActivity.createProgressDialog("Encoding video");
progressDialog = DialogUtil.createProgressDialog(requireContext(), "Encoding video");
selectedCodec = getResources().getStringArray(R.array.video_codec)[0];
playerSession = new PlayerSession(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, getContext(), getVideoFile().getAbsolutePath());
playerSurface = view.findViewById(R.id.videoPlayerFrame);
playerSurface.init(getActivity(), new GenericMotionListener(), playerSession);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
setActive();
}
}
public void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
public static VideoTabFragment newInstance(final MainActivity mainActivity) {
final VideoTabFragment fragment = new VideoTabFragment();
fragment.setMainActivity(mainActivity);
return fragment;
public static VideoTabFragment newInstance() {
return new VideoTabFragment();
}
public void enableLogCallback() {
@@ -158,15 +147,15 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
}
public void encodeVideo() {
final File image1File = new File(mainActivity.getCacheDir(), "colosseum.jpg");
final File image2File = new File(mainActivity.getCacheDir(), "pyramid.jpg");
final File image3File = new File(mainActivity.getCacheDir(), "tajmahal.jpg");
final File image1File = new File(requireContext().getCacheDir(), "colosseum.jpg");
final File image2File = new File(requireContext().getCacheDir(), "pyramid.jpg");
final File image3File = new File(requireContext().getCacheDir(), "tajmahal.jpg");
final File videoFile = getVideoFile();
try {
// IF VIDEO IS PLAYING STOP PLAYBACK
videoView.stopPlayback();
// videoView.stopPlayback();
if (videoFile.exists()) {
videoFile.delete();
@@ -178,15 +167,15 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
showProgressDialog();
mainActivity.resourceToFile(R.drawable.colosseum, image1File);
mainActivity.resourceToFile(R.drawable.pyramid, image2File);
mainActivity.resourceToFile(R.drawable.tajmahal, image3File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.colosseum, image1File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.pyramid, image2File);
ResourcesUtil.resourceToFile(getResources(), R.drawable.tajmahal, image3File);
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath(), getSelectedVideoCodec(), getCustomOptions());
Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'", ffmpegCommand));
MainActivity.executeAsync(new ExecuteCallback() {
MainActivity.executeAsync(new SingleExecuteCallback() {
@Override
public void apply(final int returnCode, final String commandOutput) {
@@ -194,7 +183,7 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
Log.d(TAG, "FFmpeg process output:");
FFmpeg.printLastCommandOutput(Log.INFO);
Config.printLastCommandOutput(Log.INFO);
hideProgressDialog();
@@ -206,7 +195,7 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
Log.d(TAG, "Encode completed successfully; playing video.");
playVideo();
} else {
Popup.show(mainActivity, "Encode failed. Please check log for the details.");
Popup.show(requireContext(), "Encode failed. Please check log for the details.");
Log.d(TAG, String.format("Encode failed with rc=%d", returnCode));
}
@@ -218,16 +207,16 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
} catch (IOException e) {
Log.e(TAG, "Encode video failed", e);
Popup.show(mainActivity, "Encode video failed");
Popup.show(requireContext(), "Encode video failed");
}
}
protected void encodeWebp() {
final File imageFile = new File(mainActivity.getCacheDir(), "colosseum.jpg");
final File outputFile = new File(mainActivity.getFilesDir(), "video.webp");
final File imageFile = new File(requireContext().getCacheDir(), "colosseum.jpg");
final File outputFile = new File(requireContext().getFilesDir(), "video.webp");
try {
mainActivity.resourceToFile(R.drawable.colosseum, imageFile);
ResourcesUtil.resourceToFile(getResources(), R.drawable.colosseum, imageFile);
Log.d(TAG, "Testing VIDEO encoding with 'webp' codec");
@@ -240,12 +229,12 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
Log.d(TAG, String.format("FFmpeg process exited with rc %d", returnCode));
} catch (IOException e) {
Log.e(TAG, "Encode webp failed", e);
Popup.show(mainActivity, "Encode webp failed");
Popup.show(requireContext(), "Encode webp failed");
}
}
protected void playVideo() {
MediaController mediaController = new MediaController(mainActivity);
/* MediaController mediaController = new MediaController(requireContext());
mediaController.setAnchorView(videoView);
videoView.setVideoURI(Uri.parse("file://" + getVideoFile().getAbsolutePath()));
videoView.setMediaController(mediaController);
@@ -265,7 +254,12 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
return false;
}
});
videoView.start();
videoView.start();*/
// Intent intent = new Intent(getContext(), FullScreenActivity.class);
// intent.putExtra(PlayerSession.FFPLAY_COMMAND, getVideoFile().getAbsolutePath());
// startActivity(intent);
playerSurface.play();
}
public String getSelectedVideoCodec() {
@@ -331,7 +325,7 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
}
final String video = "video." + extension;
return new File(mainActivity.getFilesDir(), video);
return new File(requireContext().getFilesDir(), video);
}
public String getCustomOptions() {
@@ -358,10 +352,10 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
}
public void setActive() {
android.util.Log.i(MainActivity.TAG, "Video Tab Activated");
Log.i(MainActivity.TAG, "Video Tab Activated");
enableLogCallback();
enableStatisticsCallback();
Popup.show(mainActivity, Tooltip.VIDEO_TEST_TOOLTIP_TEXT);
Popup.show(requireContext(), Tooltip.VIDEO_TEST_TOOLTIP_TEXT);
}
protected void showProgressDialog() {
@@ -398,10 +392,47 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
@Override
public Object call() {
VideoTabFragment.this.progressDialog = mainActivity.createProgressDialog("Encoding video");
VideoTabFragment.this.progressDialog = DialogUtil.createProgressDialog(requireContext(), "Encoding video");
return null;
}
});
}
@Override
public void onPause() {
Log.v(Config.TAG, "VideoTabFragment paused.");
playerSurface.setNextNativeState(PlayerSession.NativeState.PAUSED);
playerSurface.setResumedCalled(false);
playerSurface.handleNativeState();
super.onPause();
}
@Override
public void onResume() {
Log.v(Config.TAG, "VideoTabFragment resumed.");
setActive();
playerSurface.setNextNativeState(PlayerSession.NativeState.RESUMED);
playerSurface.setResumedCalled(true);
playerSurface.handleNativeState();
super.onResume();
}
@Override
public void onLowMemory() {
Log.v(Config.TAG, "VideoTabFragment is on low memory.");
FFplay.playerNativeLowMemory();
super.onLowMemory();
}
@Override
public void onDestroy() {
Log.v(Config.TAG, "VideoTabFragment destroyed.");
playerSurface.setNextNativeState(PlayerSession.NativeState.PAUSED);
playerSurface.handleNativeState();
// Send a quit message to the application
FFplay.playerNativeQuit();
super.onDestroy();
}
}
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2019 Alexander Berezhnoi
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.util;
import android.app.AlertDialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.arthenica.mobileffmpeg.test.R;
public class DialogUtil {
public static AlertDialog createProgressDialog(Context context, final String text) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (inflater != null) {
View dialogView = inflater.inflate(R.layout.progress_dialog_layout, null);
builder.setView(dialogView);
TextView textView = dialogView.findViewById(R.id.progressDialogText);
if (textView != null) {
textView.setText(text);
}
}
builder.setCancelable(false);
return builder.create();
}
public static AlertDialog createCancellableProgressDialog(Context context, final String text, final View.OnClickListener onClickListener) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (inflater != null) {
View dialogView = inflater.inflate(R.layout.cancellable_progress_dialog_layout, null);
builder.setView(dialogView);
TextView textView = dialogView.findViewById(R.id.progressDialogText);
if (textView != null) {
textView.setText(text);
}
Button cancelButton = dialogView.findViewById(R.id.cancelButton);
if (cancelButton != null) {
cancelButton.setOnClickListener(onClickListener);
}
}
builder.setCancelable(false);
return builder.create();
}
}
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2019 Alexander Berezhnoi
*
* This file is part of MobileFFmpeg.
*
* MobileFFmpeg 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.
*
* MobileFFmpeg 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 MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
*/
package com.arthenica.mobileffmpeg.util;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import com.arthenica.mobileffmpeg.test.MainActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class ResourcesUtil {
public static void resourceToFile(Resources resources, final int resourceId, final File file) throws IOException {
Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId);
if (file.exists()) {
file.delete();
}
FileOutputStream outputStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.flush();
outputStream.close();
}
public static void rawResourceToFile(Resources resources, final int resourceId, final File file) throws IOException {
final InputStream inputStream = resources.openRawResource(resourceId);
if (file.exists()) {
file.delete();
}
final FileOutputStream outputStream = new FileOutputStream(file);
try {
final byte[] buffer = new byte[1024];
int readSize;
while ((readSize = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, readSize);
}
} catch (final IOException e) {
Log.e(MainActivity.TAG, "Saving raw resource failed.", e);
} finally {
inputStream.close();
outputStream.flush();
outputStream.close();
}
}
}
@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical">
<android.support.v7.widget.AppCompatTextView
android:id="@+id/header"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/app_name"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat"
android:textColor="@android:color/white"
android:textStyle="bold"
app:fontFamily="sans-serif" />
</LinearLayout>
@@ -1,33 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<androidx.viewpager.widget.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="207dp"
tools:context="com.arthenica.mobileffmpeg.test.MainActivity"
tools:layout_editor_absoluteY="81dp">
tools:context="com.arthenica.mobileffmpeg.test.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="0dp"
<androidx.viewpager.widget.PagerTabStrip
android:id="@+id/pagerTabStrip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
android:layout_gravity="top"/>
<android.support.v4.view.PagerTabStrip
android:id="@+id/pagerTabStrip"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="top"
android:layout_marginBottom="30dp"
android:layout_marginTop="0dp"
android:padding="0dp" />
</android.support.v4.view.ViewPager>
</android.support.constraint.ConstraintLayout>
</androidx.viewpager.widget.ViewPager>
@@ -22,15 +22,15 @@
tools:targetApi="cupcake" />
<Button
android:id="@+id/runButton"
android:layout_width="80dp"
android:id="@+id/runFFmpegButton"
android:layout_width="140dp"
android:layout_height="36dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="@drawable/rounded_button"
android:fontFamily="sans-serif"
android:gravity="center"
android:text="@string/command_run_button_text"
android:text="@string/command_run_ffmpeg_button_text"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="16sp"
@@ -38,15 +38,15 @@
tools:targetApi="jelly_bean" />
<Button
android:id="@+id/runAsyncButton"
android:id="@+id/runFFprobeButton"
android:layout_width="140dp"
android:layout_height="36dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="@drawable/rounded_button"
android:fontFamily="sans-serif"
android:gravity="center"
android:text="@string/command_run_async_button_text"
android:text="@string/command_run_ffprobe_button_text"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="16sp"
@@ -52,7 +52,7 @@
android:textStyle="bold" />
</LinearLayout>
<VideoView
<com.arthenica.mobileffmpeg.player.PlayerSurface
android:id="@+id/videoPlayerFrame"
android:layout_width="wrap_content"
android:background="@drawable/rounded_video_frame"
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="headerColor">#e74c3c</color>
<color name="headerColorDark">#DD4232</color>
<color name="editColor">#3498db</color>
<color name="navigationColor">#e67e22</color>
</resources>
@@ -8,8 +8,8 @@
<string name="vidstab_tab">VID.STAB</string>
<string name="pipe_tab">PIPE</string>
<string name="command_text_input_placeholder">Enter command</string>
<string name="command_run_async_button_text">RUN ASYNC</string>
<string name="command_run_button_text">RUN</string>
<string name="command_run_ffmpeg_button_text">RUN FFMPEG</string>
<string name="command_run_ffprobe_button_text">RUN FFPROBE</string>
<string name="video_encode_button_text">ENCODE</string>
<string name="video_create_button_text">CREATE</string>
<string name="https_get_info_button_text">GET INFO</string>
@@ -3,7 +3,7 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/headerColor</item>
<item name="colorPrimaryDark">@color/headerColor</item>
<item name="colorPrimaryDark">@color/headerColorDark</item>
<item name="colorAccent">@color/editColor</item>
</style>
+23 -20
View File
@@ -250,27 +250,27 @@ get_size_optimization_cflags() {
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -O2 -ffunction-sections -fdata-sections"
;;
*)
ARCH_OPTIMIZATION="-Os -ffunction-sections -fdata-sections"
ARCH_OPTIMIZATION="-O2 -ffunction-sections -fdata-sections"
;;
esac
;;
arm64-v8a)
case $1 in
ffmpeg)
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -fuse-ld=gold -O2 -ffunction-sections -fdata-sections"
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -fuse-ld=bfd -O2 -ffunction-sections -fdata-sections"
;;
*)
ARCH_OPTIMIZATION="-Os -ffunction-sections -fdata-sections"
ARCH_OPTIMIZATION="-O2 -ffunction-sections -fdata-sections"
;;
esac
;;
x86 | x86-64)
case $1 in
ffmpeg)
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -Os -ffunction-sections -fdata-sections"
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -O2 -ffunction-sections -fdata-sections"
;;
*)
ARCH_OPTIMIZATION="-Os -ffunction-sections -fdata-sections"
ARCH_OPTIMIZATION="-O2 -ffunction-sections -fdata-sections"
;;
esac
;;
@@ -291,15 +291,18 @@ get_app_specific_cflags() {
ffmpeg)
APP_FLAGS="-Wno-unused-function -DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD"
;;
kvazaar)
APP_FLAGS="-std=gnu99 -Wno-unused-function"
;;
shine)
APP_FLAGS="-Wno-unused-function"
;;
sdl)
APP_FLAGS="-std=c11 -Wno-unused-function"
;;
soxr | snappy | libwebp)
APP_FLAGS="-std=gnu99 -Wno-unused-function -DPIC"
;;
kvazaar)
APP_FLAGS="-std=gnu99 -Wno-unused-function"
;;
*)
APP_FLAGS="-std=c99 -Wno-unused-function"
;;
@@ -330,7 +333,7 @@ get_cxxflags() {
fi
if [[ -z ${MOBILE_FFMPEG_DEBUG} ]]; then
local OPTIMIZATION_FLAGS="-Os -ffunction-sections -fdata-sections"
local OPTIMIZATION_FLAGS="-O2 -ffunction-sections -fdata-sections"
else
local OPTIMIZATION_FLAGS="${MOBILE_FFMPEG_DEBUG}"
fi
@@ -392,10 +395,10 @@ get_size_optimization_ldflags() {
arm64-v8a)
case $1 in
ffmpeg)
echo "-Wl,--gc-sections ${LINK_TIME_OPTIMIZATION_FLAGS} -fuse-ld=gold -O2 -ffunction-sections -fdata-sections -finline-functions"
echo "-Wl,--gc-sections ${LINK_TIME_OPTIMIZATION_FLAGS} -fuse-ld=bfd -O2 -ffunction-sections -fdata-sections -finline-functions"
;;
*)
echo "-Wl,--gc-sections -Os -ffunction-sections -fdata-sections"
echo "-Wl,--gc-sections -O2 -ffunction-sections -fdata-sections"
;;
esac
;;
@@ -405,7 +408,7 @@ get_size_optimization_ldflags() {
echo "-Wl,--gc-sections,--icf=safe ${LINK_TIME_OPTIMIZATION_FLAGS} -O2 -ffunction-sections -fdata-sections -finline-functions"
;;
*)
echo "-Wl,--gc-sections,--icf=safe -Os -ffunction-sections -fdata-sections"
echo "-Wl,--gc-sections,--icf=safe -O2 -ffunction-sections -fdata-sections"
;;
esac
;;
@@ -913,20 +916,20 @@ download_gpl_library_source() {
GPL_LIB_DEST_DIR="libvidstab"
;;
x264)
GPL_LIB_URL="ftp://ftp.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-20191024-2245-stable.tar.bz2"
GPL_LIB_FILE="x264-snapshot-20191024-2245-stable.tar.bz2"
GPL_LIB_ORIG_DIR="x264-snapshot-20191024-2245-stable"
GPL_LIB_URL="https://code.videolan.org/videolan/x264/-/archive/1771b556ee45207f8711744ccbd5d42a3949b14c/x264-1771b556ee45207f8711744ccbd5d42a3949b14c.tar.bz2"
GPL_LIB_FILE="x264-1771b556ee45207f8711744ccbd5d42a3949b14c.tar.bz2"
GPL_LIB_ORIG_DIR="x264-1771b556ee45207f8711744ccbd5d42a3949b14c"
GPL_LIB_DEST_DIR="x264"
;;
x265)
GPL_LIB_URL="https://bitbucket.org/multicoreware/x265/downloads/x265_3.2.tar.gz"
GPL_LIB_FILE="x265-3.2.tar.gz"
GPL_LIB_ORIG_DIR="x265_3.2"
GPL_LIB_URL="https://bitbucket.org/multicoreware/x265/downloads/x265_3.2.1.tar.gz"
GPL_LIB_FILE="x265-3.2.1.tar.gz"
GPL_LIB_ORIG_DIR="x265_3.2.1"
GPL_LIB_DEST_DIR="x265"
;;
xvidcore)
GPL_LIB_URL="https://downloads.xvid.com/downloads/xvidcore-1.3.5.tar.gz"
GPL_LIB_FILE="xvidcore-1.3.5.tar.gz"
GPL_LIB_URL="https://downloads.xvid.com/downloads/xvidcore-1.3.7.tar.gz"
GPL_LIB_FILE="xvidcore-1.3.7.tar.gz"
GPL_LIB_ORIG_DIR="xvidcore"
GPL_LIB_DEST_DIR="xvidcore"
;;
+4
View File
@@ -336,6 +336,10 @@ export LDFLAGS="${LDFLAGS}"
# USE HIGHER LIMITS FOR FFMPEG LINKING
ulimit -n 2048 1>>${BASEDIR}/build.log 2>&1
# Workaround for issue #328
rm -f ${BASEDIR}/src/${LIB_NAME}/libswscale/aarch64/hscale.S 1>>${BASEDIR}/build.log 2>&1
cp ${BASEDIR}/tools/make/ffmpeg/libswscale/aarch64/hscale.S ${BASEDIR}/src/${LIB_NAME}/libswscale/aarch64/hscale.S 1>>${BASEDIR}/build.log 2>&1
./configure \
--cross-prefix="${TARGET_HOST}-" \
--sysroot="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot" \
+1 -1
View File
@@ -88,6 +88,6 @@ fi
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_gnutls_package_config "3.6.10"
create_gnutls_package_config "3.6.11.1"
make install || exit 1
+1 -1
View File
@@ -43,7 +43,7 @@ ${SED_INLINE} 's/\-mno-ieee-fp//g' ${BASEDIR}/src/${LIB_NAME}/configure.ac
# ALWAYS RECONFIGURE
autoreconf_library ${LIB_NAME}
./configure \
PKG_CONFIG= ./configure \
--prefix=${BASEDIR}/prebuilt/android-$(get_target_build)/${LIB_NAME} \
--with-pic \
--with-sysroot=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot \
+4
View File
@@ -32,6 +32,10 @@ export CFLAGS="$(get_cflags ${LIB_NAME}) -I${ANDROID_NDK_ROOT}/sources/android/c
export CXXFLAGS=$(get_cxxflags ${LIB_NAME})
export LDFLAGS="$(get_ldflags ${LIB_NAME}) -L${ANDROID_NDK_ROOT}/sources/android/cpufeatures -lcpufeatures"
# RECOVER configure.sh
rm -f ${BASEDIR}/src/${LIB_NAME}/build/make/configure.sh
cp ${BASEDIR}/tools/make/configure.libvpx.android.sh ${BASEDIR}/src/${LIB_NAME}/build/make/configure.sh
TARGET_CPU=""
DISABLE_NEON_FLAG=""
case ${ARCH} in
+1 -1
View File
@@ -102,6 +102,6 @@ cmake -Wno-dev \
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_libwebp_package_config "1.0.3"
create_libwebp_package_config "1.1.0"
make install || exit 1
+1 -1
View File
@@ -65,6 +65,6 @@ autoreconf_library ${LIB_NAME}
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_libxml2_package_config "2.9.9"
create_libxml2_package_config "2.9.10"
make install || exit 1
+57 -15
View File
@@ -34,28 +34,70 @@ export CXXFLAGS=$(get_cxxflags ${LIB_NAME})
export LDFLAGS=$(get_ldflags ${LIB_NAME})
export PKG_CONFIG_LIBDIR="${INSTALL_PKG_CONFIG_DIR}"
SIMD_OPTIONS=""
case ${ARCH} in
arm-v7a)
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a -DCMAKE_ANDROID_ARM_MODE=ON -DCMAKE_ANDROID_ARM_NEON=OFF -DCMAKE_SYSTEM_PROCESSOR=armv7-a"
;;
arm-v7a-neon)
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a -DCMAKE_ANDROID_ARM_MODE=ON -DCMAKE_ANDROID_ARM_NEON=ON -DCMAKE_SYSTEM_PROCESSOR=armv7-a"
;;
arm64-v8a)
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=$(get_android_arch 2) -DCMAKE_SYSTEM_PROCESSOR=$(get_cmake_target_processor)"
;;
x86)
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=$(get_android_arch 3) -DCMAKE_SYSTEM_PROCESSOR=i686"
;;
x86-64)
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=$(get_android_arch 4) -DCMAKE_SYSTEM_PROCESSOR=$(get_cmake_target_processor)"
;;
esac
cd ${BASEDIR}/src/${LIB_NAME} || exit 1
make distclean 2>/dev/null 1>/dev/null
# RECONFIGURING IF REQUESTED
if [[ ${RECONF_sdl} -eq 1 ]]; then
autoreconf_library ${LIB_NAME}
if [ -d "build" ]; then
rm -rf build
fi
./configure \
--prefix=${BASEDIR}/prebuilt/android-$(get_target_build)/${LIB_NAME} \
--with-pic \
--without-x \
--with-sysroot=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot \
--enable-static \
--disable-shared \
--disable-fast-install \
--host=${TARGET_HOST} || exit 1
mkdir build;
cd build
# USE OUR OWN IMPLEMENTATION
rm -f ${BASEDIR}/src/${LIB_NAME}/src/core/android/SDL_android.c
cp ${BASEDIR}/tools/make/sdl/SDL_android.c ${BASEDIR}/src/${LIB_NAME}/src/core/android/SDL_android.c
cmake -Wno-dev \
-DCMAKE_VERBOSE_MAKEFILE=0 \
-DCMAKE_C_FLAGS="${CFLAGS}" \
-DCMAKE_CXX_FLAGS="${CXXFLAGS}" \
-DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" \
-DCMAKE_SYSROOT="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot" \
-DCMAKE_FIND_ROOT_PATH="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/${TARGET_HOST}/lib;${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot/usr/lib/${TARGET_HOST}/${API};${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/lib" \
-DCMAKE_ANDROID_NDK=${ANDROID_NDK_ROOT} \
-DCMAKE_SYSTEM_LIBRARY_PATH="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/${TARGET_HOST}/lib;${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot/usr/lib/${TARGET_HOST}/${API};${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/lib" \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_ANDROID_API="${API}" \
-DCMAKE_ANDROID_NDK="${ANDROID_NDK_ROOT}" \
${SIMD_OPTIONS} \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${BASEDIR}/prebuilt/android-$(get_target_build)/${LIB_NAME}" \
-DCMAKE_LINKER="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin/$LD" \
-DCMAKE_AR="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin/$AR" \
-DCMAKE_AS="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin/$AS" \
-DCMAKE_RANLIB="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin/$RANLIB" \
-DSDL_SHARED=0 \
-DSDL_STATIC=1 \
-DSDL_STATIC_PIC=1 .. || exit 1
make -j$(get_cpu_count) || exit 1
# WORKAROUND FOR LINKERS THAT CAN NOT USE FULL PATHS i.e. arm64-v8a LINKER
${SED_INLINE} "s/\.so / /g" sdl2.pc
${SED_INLINE} "s/${ANDROID_NDK_ROOT//\//\\/}\/toolchains\/llvm\/prebuilt\/${TOOLCHAIN//\//\\/}\/${TARGET_HOST//\//\\/}\/lib\/lib//g" sdl2.pc
${SED_INLINE} "s/${ANDROID_NDK_ROOT//\//\\/}\/toolchains\/llvm\/prebuilt\/${TOOLCHAIN//\//\\/}\/sysroot\/usr\/lib\/${TARGET_HOST//\//\\/}\/${API}\/lib//g" sdl2.pc
${SED_INLINE} "s/${ANDROID_NDK_ROOT//\//\\/}\/toolchains\/llvm\/prebuilt\/${TOOLCHAIN//\//\\/}\/lib\/lib//g" sdl2.pc
# MANUALLY COPY PKG-CONFIG FILES
cp ./*.pc ${INSTALL_PKG_CONFIG_DIR} || exit 1
cp *.pc ${INSTALL_PKG_CONFIG_DIR} || exit 1
make install || exit 1
+4 -1
View File
@@ -65,6 +65,9 @@ fi
mkdir cmake-build || exit 1
cd cmake-build || exit 1
# FIX static_assert ERRORS
${SED_INLINE} 's/gnu++98/c++11/g' ${BASEDIR}/src/${LIB_NAME}/source/CMakeLists.txt
cmake -Wno-dev \
-DCMAKE_VERBOSE_MAKEFILE=0 \
-DCMAKE_C_FLAGS="${CFLAGS}" \
@@ -91,6 +94,6 @@ cmake -Wno-dev \
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_x265_package_config "3.2"
create_x265_package_config "3.2.1"
make install || exit 1
+1 -1
View File
@@ -60,7 +60,7 @@ esac
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_xvidcore_package_config "1.3.5"
create_xvidcore_package_config "1.3.6"
make install || exit 1
+85 -80
View File
@@ -25,47 +25,47 @@ get_library_name() {
4) echo "gnutls" ;;
5) echo "lame" ;;
6) echo "libass" ;;
7) echo "libiconv" ;;
8) echo "libtheora" ;;
9) echo "libvorbis" ;;
10) echo "libvpx" ;;
11) echo "libwebp" ;;
12) echo "libxml2" ;;
13) echo "opencore-amr" ;;
14) echo "shine" ;;
15) echo "speex" ;;
16) echo "wavpack" ;;
17) echo "kvazaar" ;;
18) echo "x264" ;;
19) echo "xvidcore" ;;
20) echo "x265" ;;
21) echo "libvidstab" ;;
22) echo "libilbc" ;;
23) echo "opus" ;;
24) echo "snappy" ;;
25) echo "soxr" ;;
26) echo "libaom" ;;
27) echo "chromaprint" ;;
28) echo "twolame" ;;
29) echo "sdl" ;;
30) echo "tesseract" ;;
31) echo "openh264" ;;
32) echo "giflib" ;;
33) echo "jpeg" ;;
34) echo "libogg" ;;
35) echo "libpng" ;;
36) echo "libuuid" ;;
37) echo "nettle" ;;
38) echo "tiff" ;;
39) echo "expat" ;;
40) echo "libsndfile" ;;
41) echo "leptonica" ;;
42) echo "ios-zlib" ;;
43) echo "ios-audiotoolbox" ;;
44) echo "ios-coreimage" ;;
45) echo "ios-bzip2" ;;
46) echo "ios-videotoolbox" ;;
47) echo "ios-avfoundation" ;;
7) echo "libtheora" ;;
8) echo "libvorbis" ;;
9) echo "libvpx" ;;
10) echo "libwebp" ;;
11) echo "libxml2" ;;
12) echo "opencore-amr" ;;
13) echo "shine" ;;
14) echo "speex" ;;
15) echo "wavpack" ;;
16) echo "kvazaar" ;;
17) echo "x264" ;;
18) echo "xvidcore" ;;
19) echo "x265" ;;
20) echo "libvidstab" ;;
21) echo "libilbc" ;;
22) echo "opus" ;;
23) echo "snappy" ;;
24) echo "soxr" ;;
25) echo "libaom" ;;
26) echo "chromaprint" ;;
27) echo "twolame" ;;
28) echo "sdl" ;;
29) echo "tesseract" ;;
30) echo "openh264" ;;
31) echo "giflib" ;;
32) echo "jpeg" ;;
33) echo "libogg" ;;
34) echo "libpng" ;;
35) echo "libuuid" ;;
36) echo "nettle" ;;
37) echo "tiff" ;;
38) echo "expat" ;;
39) echo "libsndfile" ;;
40) echo "leptonica" ;;
41) echo "ios-zlib" ;;
42) echo "ios-audiotoolbox" ;;
43) echo "ios-coreimage" ;;
44) echo "ios-bzip2" ;;
45) echo "ios-videotoolbox" ;;
46) echo "ios-avfoundation" ;;
47) echo "ios-libiconv" ;;
esac
}
@@ -73,21 +73,21 @@ get_package_config_file_name() {
case $1 in
1) echo "freetype2" ;;
5) echo "libmp3lame" ;;
8) echo "theora" ;;
9) echo "vorbis" ;;
10) echo "vpx" ;;
12) echo "libxml-2.0" ;;
13) echo "opencore-amrnb" ;;
21) echo "vidstab" ;;
26) echo "aom" ;;
27) echo "libchromaprint" ;;
29) echo "sdl2" ;;
33) echo "libjpeg" ;;
34) echo "ogg" ;;
36) echo "uuid" ;;
38) echo "libtiff-4" ;;
40) echo "sndfile" ;;
41) echo "lept" ;;
7) echo "theora" ;;
8) echo "vorbis" ;;
9) echo "vpx" ;;
11) echo "libxml-2.0" ;;
12) echo "opencore-amrnb" ;;
20) echo "vidstab" ;;
25) echo "aom" ;;
26) echo "libchromaprint" ;;
28) echo "sdl2" ;;
32) echo "libjpeg" ;;
33) echo "ogg" ;;
35) echo "uuid" ;;
37) echo "libtiff-4" ;;
39) echo "sndfile" ;;
40) echo "lept" ;;
*) echo $(get_library_name $1)
esac
}
@@ -100,20 +100,20 @@ get_static_archive_name() {
case $1 in
5) echo "libmp3lame.a" ;;
6) echo "libass.a" ;;
10) echo "libvpx.a" ;;
12) echo "libxml2.a" ;;
21) echo "libvidstab.a" ;;
22) echo "libilbc.a" ;;
26) echo "libaom.a" ;;
28) echo "libtwolame.a" ;;
29) echo "libSDL2.a" ;;
30) echo "libtesseract.a" ;;
32) echo "libgif.a" ;;
34) echo "libogg.a" ;;
35) echo "libpng.a" ;;
36) echo "libuuid.a" ;;
40) echo "libsndfile.a" ;;
41) echo "liblept.a" ;;
9) echo "libvpx.a" ;;
11) echo "libxml2.a" ;;
20) echo "libvidstab.a" ;;
21) echo "libilbc.a" ;;
25) echo "libaom.a" ;;
27) echo "libtwolame.a" ;;
28) echo "libSDL2.a" ;;
29) echo "libtesseract.a" ;;
31) echo "libgif.a" ;;
33) echo "libogg.a" ;;
34) echo "libpng.a" ;;
35) echo "libuuid.a" ;;
39) echo "libsndfile.a" ;;
40) echo "liblept.a" ;;
*) echo lib$(get_library_name $1).a
esac
}
@@ -321,7 +321,7 @@ get_app_specific_cflags() {
mobile-ffmpeg)
APP_FLAGS="-std=c99 -Wno-unused-function -Wall -Wno-deprecated-declarations -Wno-pointer-sign -Wno-switch -Wno-unused-result -Wno-unused-variable -DPIC -fobjc-arc"
;;
sdl2)
sdl)
APP_FLAGS="-DPIC -Wno-unused-function -D__IPHONEOS__"
;;
shine)
@@ -615,10 +615,10 @@ EOF
}
create_libiconv_package_config() {
local LIB_ICONV_VERSION="$1"
local LIB_ICONV_VERSION=$(grep '_LIBICONV_VERSION' ${SDK_PATH}/usr/include/iconv.h | grep -Eo '0x.*' | grep -Eo '.* ')
cat > "${INSTALL_PKG_CONFIG_DIR}/libiconv.pc" << EOF
prefix=${BASEDIR}/prebuilt/$(get_target_build_directory)/libiconv
prefix=${SDK_PATH}/usr
exec_prefix=\${prefix}
libdir=\${exec_prefix}/lib
includedir=\${prefix}/include
@@ -923,20 +923,20 @@ download_gpl_library_source() {
GPL_LIB_DEST_DIR="libvidstab"
;;
x264)
GPL_LIB_URL="ftp://ftp.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-20191024-2245-stable.tar.bz2"
GPL_LIB_FILE="x264-snapshot-20191024-2245-stable.tar.bz2"
GPL_LIB_ORIG_DIR="x264-snapshot-20191024-2245-stable"
GPL_LIB_URL="https://code.videolan.org/videolan/x264/-/archive/1771b556ee45207f8711744ccbd5d42a3949b14c/x264-1771b556ee45207f8711744ccbd5d42a3949b14c.tar.bz2"
GPL_LIB_FILE="x264-1771b556ee45207f8711744ccbd5d42a3949b14c.tar.bz2"
GPL_LIB_ORIG_DIR="x264-1771b556ee45207f8711744ccbd5d42a3949b14c"
GPL_LIB_DEST_DIR="x264"
;;
x265)
GPL_LIB_URL="https://bitbucket.org/multicoreware/x265/downloads/x265_3.2.tar.gz"
GPL_LIB_URL="https://bitbucket.org/multicoreware/x265/downloads/x265_3.2.1.tar.gz"
GPL_LIB_FILE="x265-3.2.tar.gz"
GPL_LIB_ORIG_DIR="x265_3.2"
GPL_LIB_ORIG_DIR="x265_3.2.1"
GPL_LIB_DEST_DIR="x265"
;;
xvidcore)
GPL_LIB_URL="https://downloads.xvid.com/downloads/xvidcore-1.3.5.tar.gz"
GPL_LIB_FILE="xvidcore-1.3.5.tar.gz"
GPL_LIB_URL="https://downloads.xvid.com/downloads/xvidcore-1.3.7.tar.gz"
GPL_LIB_FILE="xvidcore-1.3.7.tar.gz"
GPL_LIB_ORIG_DIR="xvidcore"
GPL_LIB_DEST_DIR="xvidcore"
;;
@@ -1067,6 +1067,7 @@ set_toolchain_clang_paths() {
export INSTALL_PKG_CONFIG_DIR="${BASEDIR}/prebuilt/$(get_target_build_directory)/pkgconfig"
export ZLIB_PACKAGE_CONFIG_PATH="${INSTALL_PKG_CONFIG_DIR}/zlib.pc"
export BZIP2_PACKAGE_CONFIG_PATH="${INSTALL_PKG_CONFIG_DIR}/bzip2.pc"
export LIB_ICONV_PACKAGE_CONFIG_PATH="${INSTALL_PKG_CONFIG_DIR}/libiconv.pc"
if [ ! -d ${INSTALL_PKG_CONFIG_DIR} ]; then
mkdir -p ${INSTALL_PKG_CONFIG_DIR}
@@ -1076,6 +1077,10 @@ set_toolchain_clang_paths() {
create_zlib_package_config
fi
if [ ! -f ${LIB_ICONV_PACKAGE_CONFIG_PATH} ]; then
create_libiconv_package_config
fi
if [ ! -f ${BZIP2_PACKAGE_CONFIG_PATH} ]; then
create_bzip2_package_config
fi
+17 -15
View File
@@ -152,11 +152,6 @@ do
FFMPEG_LDFLAGS+=" $(pkg-config --libs --static libass)"
CONFIGURE_POSTFIX+=" --enable-libass"
;;
libiconv)
FFMPEG_CFLAGS+=" $(pkg-config --cflags libiconv)"
FFMPEG_LDFLAGS+=" $(pkg-config --libs --static libiconv)"
CONFIGURE_POSTFIX+=" --enable-iconv"
;;
libilbc)
FFMPEG_CFLAGS+=" $(pkg-config --cflags libilbc)"
FFMPEG_LDFLAGS+=" $(pkg-config --libs --static libilbc)"
@@ -315,28 +310,31 @@ do
*-zlib)
CONFIGURE_POSTFIX+=" --enable-zlib"
;;
*-libiconv)
CONFIGURE_POSTFIX+=" --enable-iconv"
;;
esac
;;
esac
else
# THE FOLLOWING LIBRARIES SHOULD BE EXPLICITLY DISABLED TO PREVENT AUTODETECT
if [[ ${library} -eq 8 ]]; then
CONFIGURE_POSTFIX+=" --disable-iconv"
elif [[ ${library} -eq 30 ]]; then
if [[ ${library} -eq 29 ]]; then
CONFIGURE_POSTFIX+=" --disable-sdl2"
elif [[ ${library} -eq 43 ]]; then
elif [[ ${library} -eq 42 ]]; then
CONFIGURE_POSTFIX+=" --disable-zlib"
elif [[ ${library} -eq 44 ]]; then
elif [[ ${library} -eq 43 ]]; then
CONFIGURE_POSTFIX+=" --disable-audiotoolbox"
elif [[ ${library} -eq 45 ]]; then
elif [[ ${library} -eq 44 ]]; then
CONFIGURE_POSTFIX+=" --disable-coreimage"
elif [[ ${library} -eq 46 ]]; then
elif [[ ${library} -eq 45 ]]; then
CONFIGURE_POSTFIX+=" --disable-bzlib"
elif [[ ${library} -eq 47 ]]; then
elif [[ ${library} -eq 46 ]]; then
CONFIGURE_POSTFIX+=" --disable-videotoolbox"
elif [[ ${library} -eq 48 ]]; then
elif [[ ${library} -eq 47 ]]; then
CONFIGURE_POSTFIX+=" --disable-avfoundation"
elif [[ ${library} -eq 48 ]]; then
CONFIGURE_POSTFIX+=" --disable-iconv"
fi
fi
@@ -396,7 +394,11 @@ if [[ -z ${NO_WORKSPACE_CLEANUP_ffmpeg} ]]; then
make distclean 2>/dev/null 1>/dev/null
fi
${SED_INLINE} 's/check_cflags -mdynamic-no-pic && add_asflags -mdynamic-no-pic;/check_cflags -mdynamic-no-pic;/g' ./configure
${SED_INLINE} 's/check_cflags -mdynamic-no-pic && add_asflags -mdynamic-no-pic;/check_cflags -mdynamic-no-pic;/g' ./configure 1>>${BASEDIR}/build.log 2>&1
# Workaround for issue #328
rm -f ${BASEDIR}/src/${LIB_NAME}/libswscale/aarch64/hscale.S 1>>${BASEDIR}/build.log 2>&1
cp ${BASEDIR}/tools/make/ffmpeg/libswscale/aarch64/hscale.S ${BASEDIR}/src/${LIB_NAME}/libswscale/aarch64/hscale.S 1>>${BASEDIR}/build.log 2>&1
./configure \
--sysroot=${SDK_PATH} \
+1 -1
View File
@@ -50,7 +50,7 @@ fi
./configure \
--prefix=${BASEDIR}/prebuilt/$(get_target_build_directory)/${LIB_NAME} \
--with-pic \
--with-libiconv-prefix=${BASEDIR}/prebuilt/$(get_target_build_directory)/libiconv \
--with-libiconv-prefix=${SDK_PATH}/usr \
--with-expat=${BASEDIR}/prebuilt/$(get_target_build_directory)/expat \
--without-libintl-prefix \
--enable-static \
+3 -3
View File
@@ -37,9 +37,9 @@ COMMON_CFLAGS=$(get_cflags ${LIB_NAME})
COMMON_CXXFLAGS=$(get_cxxflags ${LIB_NAME})
COMMON_LDFLAGS=$(get_ldflags ${LIB_NAME})
export CFLAGS="${COMMON_CFLAGS} -I${BASEDIR}/prebuilt/$(get_target_build_directory)/libiconv/include"
export CFLAGS="${COMMON_CFLAGS} -I${SDK_PATH}/usr/include"
export CXXFLAGS="${COMMON_CXXFLAGS}"
export LDFLAGS="${COMMON_LDFLAGS} -L${BASEDIR}/prebuilt/$(get_target_build_directory)/libiconv/lib"
export LDFLAGS="${COMMON_LDFLAGS} -L${SDK_PATH}/usr/lib"
export NETTLE_CFLAGS="-I${BASEDIR}/prebuilt/$(get_target_build_directory)/nettle/include"
export NETTLE_LIBS="-L${BASEDIR}/prebuilt/$(get_target_build_directory)/nettle/lib -lnettle -L${BASEDIR}/prebuilt/$(get_target_build_directory)/gmp/lib -lgmp"
@@ -101,6 +101,6 @@ fi
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_gnutls_package_config "3.6.10"
create_gnutls_package_config "3.6.11.1"
make install || exit 1
+1 -1
View File
@@ -51,7 +51,7 @@ fi
--prefix=${BASEDIR}/prebuilt/$(get_target_build_directory)/${LIB_NAME} \
--with-pic \
--with-sysroot=${SDK_PATH} \
--with-libiconv-prefix=${BASEDIR}/prebuilt/$(get_target_build_directory)/libiconv \
--with-libiconv-prefix=${SDK_PATH}/usr \
--enable-static \
--disable-shared \
--disable-fast-install \
-64
View File
@@ -1,64 +0,0 @@
#!/bin/bash
if [[ -z ${ARCH} ]]; then
echo -e "(*) ARCH not defined\n"
exit 1
fi
if [[ -z ${TARGET_SDK} ]]; then
echo -e "(*) TARGET_SDK not defined\n"
exit 1
fi
if [[ -z ${SDK_PATH} ]]; then
echo -e "(*) SDK_PATH not defined\n"
exit 1
fi
if [[ -z ${BASEDIR} ]]; then
echo -e "(*) BASEDIR not defined\n"
exit 1
fi
# ENABLE COMMON FUNCTIONS
if [[ ${APPLE_TVOS_BUILD} -eq 1 ]]; then
. ${BASEDIR}/build/tvos-common.sh
else
. ${BASEDIR}/build/ios-common.sh
fi
# PREPARING PATHS & DEFINING ${INSTALL_PKG_CONFIG_DIR}
LIB_NAME="libiconv"
set_toolchain_clang_paths ${LIB_NAME}
# PREPARING FLAGS
TARGET_HOST=$(get_target_host)
export CFLAGS=$(get_cflags ${LIB_NAME})
export CXXFLAGS=$(get_cxxflags ${LIB_NAME})
export LDFLAGS=$(get_ldflags ${LIB_NAME})
cd ${BASEDIR}/src/${LIB_NAME} || exit 1
make distclean 2>/dev/null 1>/dev/null
# RECONFIGURING IF REQUESTED
if [[ ${RECONF_libiconv} -eq 1 ]]; then
autoreconf_library ${LIB_NAME}
fi
./configure \
--prefix=${BASEDIR}/prebuilt/$(get_target_build_directory)/${LIB_NAME} \
--with-pic \
--with-sysroot=${SDK_PATH} \
--enable-static \
--disable-shared \
--disable-fast-install \
--disable-rpath \
--host=${TARGET_HOST} || exit 1
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_libiconv_package_config "1.16"
make install || exit 1
+3 -1
View File
@@ -51,8 +51,10 @@ fi
# ld: -force_cpusubtype_ALL and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together
${SED_INLINE} 's/-force_cpusubtype_ALL//g' ${BASEDIR}/src/${LIB_NAME}/configure
./configure \
PKG_CONFIG= ./configure \
--prefix=${BASEDIR}/prebuilt/$(get_target_build_directory)/${LIB_NAME} \
--with-ogg-includes=${BASEDIR}/prebuilt/$(get_target_build_directory)/libogg/include \
--with-ogg-libraries=${BASEDIR}/prebuilt/$(get_target_build_directory)/libogg/lib \
--with-pic \
--with-sysroot=${SDK_PATH} \
--enable-static \
+1 -1
View File
@@ -97,6 +97,6 @@ cmake -Wno-dev \
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_libwebp_package_config "1.0.3"
create_libwebp_package_config "1.1.0"
make install || exit 1
+2 -2
View File
@@ -50,7 +50,7 @@ autoreconf_library ${LIB_NAME}
--with-pic \
--with-sysroot=${SDK_PATH} \
--with-zlib \
--with-iconv=${BASEDIR}/prebuilt/$(get_target_build_directory)/libiconv \
--with-iconv=${SDK_PATH}/usr \
--with-sax1 \
--without-python \
--without-debug \
@@ -63,6 +63,6 @@ autoreconf_library ${LIB_NAME}
make -j$(get_cpu_count) || exit 1
# CREATE PACKAGE CONFIG MANUALLY
create_libxml2_package_config "2.9.9"
create_libxml2_package_config "2.9.10"
make install || exit 1
+1 -1
View File
@@ -58,7 +58,7 @@ if [[ ${RECONF_mobile_ffmpeg} -eq 1 ]]; then
fi
VIDEOTOOLBOX_SUPPORT_FLAG=""
if [[ ${47} -eq 1 ]]; then
if [[ ${46} -eq 1 ]]; then
VIDEOTOOLBOX_SUPPORT_FLAG="--enable-videotoolbox"
fi
+11
View File
@@ -46,11 +46,22 @@ if [[ ${RECONF_wavpack} -eq 1 ]]; then
autoreconf_library ${LIB_NAME}
fi
ASM_FLAGS=""
case ${ARCH} in
armv7 | armv7s | arm64 | arm64e)
ASM_FLAGS="--disable-asm"
;;
*)
ASM_FLAGS="--enable-asm"
;;
esac
./configure \
--prefix=${BASEDIR}/prebuilt/$(get_target_build_directory)/${LIB_NAME} \
--with-pic \
--with-sysroot=${SDK_PATH} \
--without-iconv \
${ASM_FLAGS} \
--enable-static \
--disable-shared \
--disable-apps \

Some files were not shown because too many files have changed in this diff Show More