Compare commits

...

35 Commits

Author SHA1 Message Date
Taner Sener 885811cb24 update github action runners 9 months ago
Taner Sener 9e6b83c737 implement native deleteSession method, fixes #1066 9 months ago
Taner Sener 8fdb3671f3 drop hard-coded lts release specific rules in build scripts 9 months ago
Taner Sener 3a752ba728 fix linux api errors 9 months ago
Taner Sener 913e5b78e4 support 16kb page sizes in android, fixes #1000 and #1012 9 months ago
Taner Sener 8a7d51cc54 upgrade android agp plugin 9 months ago
Taner Sener 07f7f32a70 fix build warnings/errors 9 months ago
Taner Sener d8464bd02a disable api version dependent filters 9 months ago
Taner Sener bfc6c95a5e improve error reporting in scripts 9 months ago
Taner Sener f86a678a96 undo the libogg workaround to fix libvorbis builds on apple 9 months ago
Taner Sener aa73d96c7c upgrade ffmpeg-kit api to 6.1.2 9 months ago
Taner Sener a68a8c47a4 fix application.mk paths 9 months ago
Taner Sener b476453c5f allow defining package names on compile time 9 months ago
Taner Sener 2e9fe1f3ce use api/min sdk specific build directories 9 months ago
Taner Sener 1a3f08faeb allow overriding job count, fixes #1054 9 months ago
Taner Sener c46ff1f87a update external library versions, fixes #1050 9 months ago
Taner Sener 346add43b3
Merge pull request #1064 from arthenica/main-doc-update 9 months ago
Taner Sener 8d2c693034 allow extra cflags, cxxflags, ldflags 1 year ago
Taner Sener 17cfcbbcb7 re-purpose android-lts-support package as android-support package 1 year ago
Taner Sener 47eb7d32a2 allow defining a custom toolkit for android and enable asm for 64 bit architectures with x265 1 year ago
Taner Sener e4476c2fc5 apply a workaround for libpng on xcode 13.4.1 1 year ago
Taner Sener 6eaadd10b5 reorganise android cflags for x264 1 year ago
Taner Sener f0f16b792f Merge branch 'main' into development 1 year ago
Taner Sener b8bbee06d6 workaround for xcode 15.0 and 15.0.1, fixes #900 #944 1 year ago
Taner Sener fabab45e91 update copyright headers of the recently updated files 1 year ago
Taner Sener fb99d7466c migrate Level enum to NSInteger, fixes #942 1 year ago
Taner Sener 154ef20443 support android ndk 26.x, fixes #850 #909 #937 1 year ago
Taner Sener c6e08c09ac implement reusable saf urls, fixes #911 1 year ago
Taner Sener b4130e72cd allow saf urls in hls playlists, fixes #894 1 year ago
Taner Sener b72814755b replace exit calls with exit_program, fixes #874 1 year ago
Taner Sener d23ace64e8 add ios png compression tip screenshot 1 year ago
Taner Sener 81fe2793e4 remove extra new lines 2 years ago
Taner Sener f0d47540c1
Merge pull request #846 from Vorticity-Flux/main 2 years ago
Artyom Zavrin e53cad6404 Fix error texts in saf_open/saf_close 2 years ago
Artyom Zavrin 1b406dcc6b Fix Segfault if SAF is used with concat demuxer 2 years ago
  1. 18
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 26
      .github/workflows/android-build-scripts.yml
  3. 12
      .github/workflows/ios-build-scripts.yml
  4. 36
      .github/workflows/linux-build-scripts.yml
  5. 12
      .github/workflows/macos-build-scripts.yml
  6. 10
      .github/workflows/periodic-builds-android.yml
  7. 36
      .github/workflows/periodic-builds-apple.yml
  8. 12
      .github/workflows/tvos-build-scripts.yml
  9. 1
      .gitignore
  10. 93
      android.sh
  11. 2
      android/build.gradle
  12. 2
      android/ffmpeg-kit-android-lib/Doxyfile
  13. 13
      android/ffmpeg-kit-android-lib/build.gradle
  14. 3
      android/ffmpeg-kit-android-lib/src/main/cpp/.gitignore
  15. 46
      android/ffmpeg-kit-android-lib/src/main/cpp/android_support.c
  16. 182
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpeg_context.c
  17. 144
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpeg_context.h
  18. 443
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.c
  19. 73
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.h
  20. 72
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_abidetect.c
  21. 23
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_abidetect.h
  22. 2
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_exception.h
  23. 25
      android/ffmpeg-kit-android-lib/src/main/cpp/ffprobekit.c
  24. 5
      android/ffmpeg-kit-android-lib/src/main/cpp/ffprobekit.h
  25. 614
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.c
  26. 190
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.h
  27. 5466
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.c
  28. 711
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.h
  29. 1168
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_dec.c
  30. 1611
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_demux.c
  31. 962
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_enc.c
  32. 3126
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_filter.c
  33. 326
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_hw.c
  34. 518
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.c
  35. 141
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.h
  36. 2722
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux_init.c
  37. 929
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_opt.c
  38. 3880
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffprobe.c
  39. 14
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_fopen_utf8.h
  40. 57
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.c
  41. 17
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.h
  42. 826
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.c
  43. 8
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.h
  44. 482
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.c
  45. 42
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.h
  46. 44
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.c
  47. 10
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.h
  48. 7
      android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/AbiDetect.java
  49. 102
      android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java
  50. 20
      android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/NativeLoader.java
  51. 4
      android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/Packages.java
  52. 6
      android/ffmpeg-kit-android-lib/src/test/java/com/arthenica/ffmpegkit/FFmpegKitConfigTest.java
  53. BIN
      android/gradle/wrapper/gradle-wrapper.jar
  54. 4
      android/gradle/wrapper/gradle-wrapper.properties
  55. 281
      android/gradlew
  56. 15
      android/gradlew.bat
  57. 62
      android/jni/Android.mk
  58. 16
      android/jni/ffmpeg/Android.mk
  59. 6
      android/jni/ffmpeg/neon/Android.mk
  60. 17
      apple.sh
  61. 10
      apple/.gitignore
  62. 2
      apple/Doxyfile
  63. 1251
      apple/aclocal.m4
  64. 271
      apple/ar-lib
  65. 348
      apple/compile
  66. 1748
      apple/config.guess
  67. 1884
      apple/config.sub
  68. 4
      apple/configure.ac
  69. 791
      apple/depcomp
  70. 541
      apple/install-sh
  71. 215
      apple/missing
  72. 1
      apple/src/.gitignore
  73. 16
      apple/src/AbstractSession.h
  74. 103
      apple/src/AbstractSession.m
  75. 11
      apple/src/ArchDetect.h
  76. 113
      apple/src/ArchDetect.m
  77. 2
      apple/src/AtomicLong.m
  78. 45
      apple/src/Chapter.h
  79. 45
      apple/src/Chapter.m
  80. 180
      apple/src/FFmpegKit.h
  81. 105
      apple/src/FFmpegKit.m
  82. 291
      apple/src/FFmpegKitConfig.h
  83. 955
      apple/src/FFmpegKitConfig.m
  84. 56
      apple/src/FFmpegSession.h
  85. 99
      apple/src/FFmpegSession.m
  86. 15
      apple/src/FFmpegSessionCompleteCallback.h
  87. 347
      apple/src/FFprobeKit.h
  88. 236
      apple/src/FFprobeKit.m
  89. 16
      apple/src/FFprobeSession.h
  90. 44
      apple/src/FFprobeSession.m
  91. 21
      apple/src/FFprobeSessionCompleteCallback.h
  92. 12
      apple/src/Level.h
  93. 4
      apple/src/Log.h
  94. 4
      apple/src/Log.m
  95. 4
      apple/src/LogCallback.h
  96. 4
      apple/src/Makefile.am
  97. 1164
      apple/src/Makefile.in
  98. 71
      apple/src/MediaInformation.h
  99. 71
      apple/src/MediaInformation.m
  100. 10
      apple/src/MediaInformationJsonParser.h
  101. Some files were not shown because too many files have changed in this diff Show More

@ -23,20 +23,20 @@ Steps to reproduce the behaviour.
If applicable, add screenshots to help explain your problem.
**Logs**
Post logs here or paste them to [Ghostbin](https://ghostbin.co) and insert the link here.
Post logs here or paste them to [Ghostbin](https://ghostbin.site) and insert the link here.
**Environment**
- Platform: `Android`/`Flutter`/`iOS`/`Linux`/`macOS`/`ReactNative`/`tvOS`
- Architecture: `arm-v7a`, `arm-v7a-neon`, `arm64-v8a`, `x86`, `x86_64`, `armv7`, `armv7s`, `arm64`, `arm64-mac-catalyst`, `arm64-simulator`, `arm64e`, `i386`, `x86-64`, `x86-64-mac-catalyst`
- Version: `v5.1`/`v5.1.LTS`
- Source branch: `main`, `development`
- Xcode version: `13.4.1`
- Cocoapods version: `1.10.3`
- Android Studio version: `4.0`
- Android NDK version: `r25b`
- flutter doctor: ` `
- react-native info: ` `
- Version: ``
- Source branch: ``
- Xcode version: ``
- Cocoapods version: ``
- Android Studio version: ``
- Android NDK version: ``
- flutter doctor: ``
- react-native info: ``
**Other**
Add any other context about the problem here.

@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
ndk-version: [ 'r22b-linux-x86_64', 'r23b-linux', 'r24-linux', 'r25b-linux' ]
ndk-version: [ 'r22b-linux-x86_64', 'r25c-linux', 'r26d-linux', 'r27c-linux' ]
defaults:
run:
shell: bash
@ -56,12 +56,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-lts-on-linux:
name: android lts on linux
build-api-16-on-linux:
name: android api 16 on linux
runs-on: ubuntu-22.04
strategy:
matrix:
ndk-version: [ 'r22b-linux-x86_64', 'r23b-linux' ]
ndk-version: [ 'r22b-linux-x86_64' ]
defaults:
run:
shell: bash
@ -80,7 +80,7 @@ jobs:
unzip -q -o ndk.zip -d .ndk
echo "ANDROID_NDK_ROOT=$PWD/.ndk/$(ls .ndk)" >> $GITHUB_ENV
- name: run the build script
run: ./android.sh -d --lts --enable-android-media-codec --enable-android-zlib
run: ./android.sh -d --api-level=16 --enable-android-media-codec --enable-android-zlib
- name: print build logs
if: ${{ always() }}
run: cat build.log
@ -89,10 +89,10 @@ jobs:
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-main-on-macos:
name: android main on macos
runs-on: macos-12
runs-on: macos-14
strategy:
matrix:
ndk-version: [ 'r22b-darwin-x86_64', 'r23b-darwin', 'r24-darwin', 'r25b-darwin' ]
ndk-version: [ 'r22b-darwin-x86_64', 'r25c-darwin', 'r26d-darwin' , 'r27c-darwin' ]
defaults:
run:
shell: bash
@ -116,12 +116,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-lts-on-macos:
name: android lts on macos
runs-on: macos-12
build-api-16-on-macos:
name: android api 16 on macos
runs-on: macos-13
strategy:
matrix:
ndk-version: [ 'r22b-darwin-x86_64', 'r23b-darwin' ]
ndk-version: [ 'r22b-darwin-x86_64' ]
defaults:
run:
shell: bash
@ -138,7 +138,7 @@ jobs:
unzip -q -o ndk.zip -d .ndk
echo "ANDROID_NDK_ROOT=$PWD/.ndk/$(ls .ndk)" >> $GITHUB_ENV
- name: run the build script
run: ./android.sh -d --lts --enable-android-media-codec --enable-android-zlib
run: ./android.sh -d --api-level=16 --enable-android-media-codec --enable-android-zlib
- name: print build logs
if: ${{ always() }}
run: cat build.log
@ -150,7 +150,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
ndk-version: [ 'r22b-linux-x86_64' ]
ndk-version: [ 'r27c-linux' ]
defaults:
run:
shell: bash

@ -32,7 +32,7 @@ jobs:
runs-on: macos-14
strategy:
matrix:
xcode: [ '15.4', '16.0' ]
xcode: [ '15.3', '15.4' ]
defaults:
run:
shell: bash
@ -73,12 +73,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-lts-on-macos-monterey:
name: ios lts on monterey
runs-on: macos-12
build-min-sdk-10-on-macos-ventura:
name: ios min sdk 10 on ventura
runs-on: macos-13
strategy:
matrix:
xcode: [ '13.4.1', '14.2' ]
xcode: [ '14.3.1', '15.2' ]
defaults:
run:
shell: bash
@ -89,7 +89,7 @@ jobs:
- name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script
run: ./ios.sh --lts --enable-ios-audiotoolbox --enable-ios-bzip2 --enable-ios-libiconv --enable-ios-zlib
run: ./ios.sh --target=10 --enable-ios-audiotoolbox --enable-ios-bzip2 --enable-ios-libiconv --enable-ios-zlib
- name: print build logs
if: ${{ always() }}
run: cat build.log

@ -41,24 +41,6 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-lts-on-ubuntu-2204:
name: linux lts on ubuntu 22.04
runs-on: ubuntu-22.04
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: prerequisites
run: sudo apt-get install clang llvm lld libclang-14-dev libstdc++6 nasm autoconf automake libtool pkg-config curl git doxygen rapidjson-dev
- name: run the build script
run: ./linux.sh -d --lts
- name: print build logs
if: ${{ always() }}
run: cat build.log
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-main-on-ubuntu-2004:
name: linux main on ubuntu-20.04
runs-on: ubuntu-20.04
@ -77,21 +59,3 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-lts-on-ubuntu-2004:
name: linux lts on ubuntu 20.04
runs-on: ubuntu-20.04
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: prerequisites
run: sudo apt-get install clang llvm lld libclang-11-dev libstdc++6 nasm autoconf automake libtool pkg-config curl git doxygen rapidjson-dev
- name: run the build script
run: ./linux.sh -d --lts
- name: print build logs
if: ${{ always() }}
run: cat build.log
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'

@ -32,7 +32,7 @@ jobs:
runs-on: macos-14
strategy:
matrix:
xcode: [ '15.4', '16.0' ]
xcode: [ '15.3', '15.4' ]
defaults:
run:
shell: bash
@ -73,12 +73,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-lts-on-macos-monterey:
name: macos lts on monterey
runs-on: macos-12
build-min-sdk-1012-on-macos-ventura:
name: macos min sdk 10.12 on ventura
runs-on: macos-13
strategy:
matrix:
xcode: [ '13.4.1', '14.2' ]
xcode: [ '14.3.1', '15.2' ]
defaults:
run:
shell: bash
@ -89,7 +89,7 @@ jobs:
- name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script
run: ./macos.sh --lts --enable-macos-audiotoolbox --enable-macos-bzip2 --enable-macos-coreimage --enable-macos-libiconv --enable-macos-opencl --enable-macos-opengl --enable-macos-videotoolbox --enable-macos-zlib
run: ./macos.sh --target=10.12 --enable-macos-audiotoolbox --enable-macos-bzip2 --enable-macos-coreimage --enable-macos-libiconv --enable-macos-opencl --enable-macos-opengl --enable-macos-videotoolbox --enable-macos-zlib
- name: print build logs
if: ${{ always() }}
run: cat build.log

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
ndk-version: [ 'r22b-linux-x86_64', 'r23b-linux', 'r24-linux', 'r25b-linux' ]
ndk-version: [ 'r22b-linux-x86_64', 'r25c-linux', 'r26d-linux' , 'r27c-linux' ]
branches: [ 'main', 'development' ]
defaults:
run:
@ -43,12 +43,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-android-lts-on-linux:
name: android lts on linux
build-android-api-16-on-linux:
name: android api 16 on linux
runs-on: ubuntu-22.04
strategy:
matrix:
ndk-version: [ 'r22b-linux-x86_64', 'r23b-linux' ]
ndk-version: [ 'r22b-linux-x86_64' ]
branches: [ 'main', 'development' ]
defaults:
run:
@ -74,7 +74,7 @@ jobs:
unzip -q -o ndk.zip -d .ndk
echo "ANDROID_NDK_ROOT=$PWD/.ndk/$(ls .ndk)" >> $GITHUB_ENV
- name: run the build script
run: ./android.sh -l --full --enable-gpl --disable-lib-srt
run: ./android.sh --api-level=16 --full --enable-gpl --disable-lib-srt
- name: print build logs
if: ${{ always() }}
run: cat build.log

@ -10,7 +10,7 @@ jobs:
runs-on: macos-14
strategy:
matrix:
xcode: [ '15.4', '16.0' ]
xcode: [ '15.3', '15.4' ]
branches: [ 'main', 'development' ]
defaults:
run:
@ -31,12 +31,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-ios-lts-on-macos-monterey:
name: ios lts on monterey
runs-on: macos-12
build-ios-min-sdk-10-on-macos-ventura:
name: ios min sdk 10 on ventura
runs-on: macos-13
strategy:
matrix:
xcode: [ '13.4.1' ]
xcode: [ '15.2' ]
branches: [ 'main', 'development' ]
defaults:
run:
@ -50,7 +50,7 @@ jobs:
- name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script
run: PATH="/usr/local/opt/bison/bin:$PATH" XML_CATALOG_FILES="/usr/local/etc/xml/catalog" ./ios.sh -l --full --enable-gpl --disable-lib-srt --disable-lib-gnutls --disable-armv7s --disable-arm64e --disable-i386
run: PATH="/usr/local/opt/bison/bin:$PATH" XML_CATALOG_FILES="/usr/local/etc/xml/catalog" ./ios.sh --target=10 --full --enable-gpl --disable-lib-srt --disable-lib-gnutls --disable-armv7s --disable-arm64e --disable-i386
- name: print build logs
if: ${{ always() }}
run: cat build.log
@ -62,7 +62,7 @@ jobs:
runs-on: macos-14
strategy:
matrix:
xcode: [ '15.4', '16.0' ]
xcode: [ '15.3', '15.4' ]
branches: [ 'main', 'development' ]
defaults:
run:
@ -83,12 +83,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-macos-lts-on-macos-monterey:
name: macos lts on monterey
runs-on: macos-12
build-macos-min-sdk-1012-on-macos-ventura:
name: macos min sdk 10.12 on ventura
runs-on: macos-13
strategy:
matrix:
xcode: [ '13.4.1' ]
xcode: [ '15.2' ]
branches: [ 'main', 'development' ]
defaults:
run:
@ -102,7 +102,7 @@ jobs:
- name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script
run: PATH="/usr/local/opt/bison/bin:$PATH" XML_CATALOG_FILES="/usr/local/etc/xml/catalog" ./macos.sh -l --full --enable-gpl --disable-lib-srt --disable-lib-gnutls
run: PATH="/usr/local/opt/bison/bin:$PATH" XML_CATALOG_FILES="/usr/local/etc/xml/catalog" ./macos.sh --target=10.12 --full --enable-gpl --disable-lib-srt --disable-lib-gnutls
- name: print build logs
if: ${{ always() }}
run: cat build.log
@ -114,7 +114,7 @@ jobs:
runs-on: macos-14
strategy:
matrix:
xcode: [ '15.4', '16.0' ]
xcode: [ '15.3', '15.4' ]
branches: [ 'main', 'development' ]
defaults:
run:
@ -135,12 +135,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-tvos-lts-on-macos-monterey:
name: tvos lts on monterey
runs-on: macos-12
build-tvos-min-sdk-100-on-macos-ventura:
name: tvos min sdk 10.0 on ventura
runs-on: macos-13
strategy:
matrix:
xcode: [ '13.4.1' ]
xcode: [ '15.2' ]
branches: [ 'main', 'development' ]
defaults:
run:
@ -154,7 +154,7 @@ jobs:
- name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script
run: PATH="/usr/local/opt/bison/bin:$PATH" XML_CATALOG_FILES="/usr/local/etc/xml/catalog" ./tvos.sh -l --full --enable-gpl --disable-lib-srt --disable-lib-gnutls
run: PATH="/usr/local/opt/bison/bin:$PATH" XML_CATALOG_FILES="/usr/local/etc/xml/catalog" ./tvos.sh --target=10.0 --full --enable-gpl --disable-lib-srt --disable-lib-gnutls
- name: print build logs
if: ${{ always() }}
run: cat build.log

@ -32,7 +32,7 @@ jobs:
runs-on: macos-14
strategy:
matrix:
xcode: [ '15.4', '16.0' ]
xcode: [ '15.3', '15.4' ]
defaults:
run:
shell: bash
@ -73,12 +73,12 @@ jobs:
- name: print ffbuild logs
if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-lts-on-macos-monterey:
name: tvos lts on monterey
runs-on: macos-12
build-min-sdk-100-on-macos-ventura:
name: tvos min sdk 10.0 on ventura
runs-on: macos-13
strategy:
matrix:
xcode: [ '13.4.1', '14.2' ]
xcode: [ '15.2' ]
defaults:
run:
shell: bash
@ -89,7 +89,7 @@ jobs:
- name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script
run: ./tvos.sh --lts --enable-tvos-bzip2 --enable-tvos-audiotoolbox --enable-tvos-libiconv --enable-tvos-zlib
run: ./tvos.sh --target=10.0 --enable-tvos-bzip2 --enable-tvos-audiotoolbox --enable-tvos-libiconv --enable-tvos-zlib
- name: print build logs
if: ${{ always() }}
run: cat build.log

1
.gitignore vendored

@ -3,3 +3,4 @@
/prebuilt/
.DS_Store
.idea
/android/jni/build.mk

@ -17,7 +17,7 @@ source "${BASEDIR}"/scripts/variable.sh
source "${BASEDIR}"/scripts/function-${FFMPEG_KIT_BUILD_TYPE}.sh
disabled_libraries=()
# SET DEFAULTS SETTINGS
# SET DEFAULT SETTINGS
enable_default_android_architectures
enable_default_android_libraries
enable_main_build
@ -34,18 +34,6 @@ BUILD_FULL=""
BUILD_TYPE_ID=""
BUILD_VERSION=$(git describe --tags --always 2>>"${BASEDIR}"/build.log)
# PROCESS LTS BUILD OPTION FIRST AND SET BUILD TYPE: MAIN OR LTS
rm -f "${BASEDIR}"/android/ffmpeg-kit-android-lib/build.gradle 1>>"${BASEDIR}"/build.log 2>&1
cp "${BASEDIR}"/tools/android/build.gradle "${BASEDIR}"/android/ffmpeg-kit-android-lib/build.gradle 1>>"${BASEDIR}"/build.log 2>&1
for argument in "$@"; do
if [[ "$argument" == "-l" ]] || [[ "$argument" == "--lts" ]]; then
enable_lts_build
BUILD_TYPE_ID+="LTS "
rm -f "${BASEDIR}"/android/ffmpeg-kit-android-lib/build.gradle 1>>"${BASEDIR}"/build.log 2>&1
cp "${BASEDIR}"/tools/android/build.lts.gradle "${BASEDIR}"/android/ffmpeg-kit-android-lib/build.gradle 1>>"${BASEDIR}"/build.log 2>&1
fi
done
# PROCESS BUILD OPTIONS
while [ ! $# -eq 0 ]; do
@ -82,10 +70,13 @@ while [ ! $# -eq 0 ]; do
-s | --speed)
optimize_for_speed
;;
-l | --lts) ;;
-f | --force)
export BUILD_FORCE="1"
;;
--jobs=*)
JOB_COUNT=$(echo $1 | sed -e 's/^--[A-Za-z]*=//g')
export BUILD_JOBS="${JOB_COUNT}"
;;
--reconf-*)
CONF_LIBRARY=$(echo $1 | sed -e 's/^--[A-Za-z]*-//g')
@ -138,6 +129,30 @@ while [ ! $# -eq 0 ]; do
--no-ffmpeg-kit-protocols)
export NO_FFMPEG_KIT_PROTOCOLS="1"
;;
--toolchain=*)
ANDROID_TOOLCHAIN=$(echo $1 | sed -e 's/^--toolchain=//g')
export ANDROID_TOOLCHAIN="${ANDROID_TOOLCHAIN}"
;;
--extra-cflags=*)
EXTRA_CFLAGS=$(echo $1 | sed -e 's/^--extra-cflags=//g')
export EXTRA_CFLAGS="${EXTRA_CFLAGS}"
;;
--extra-cxxflags=*)
EXTRA_CXXFLAGS=$(echo $1 | sed -e 's/^--extra-cxxflags=//g')
export EXTRA_CXXFLAGS="${EXTRA_CXXFLAGS}"
;;
--extra-ldflags=*)
EXTRA_LDFLAGS=$(echo $1 | sed -e 's/^--extra-ldflags=//g')
export EXTRA_LDFLAGS="${EXTRA_LDFLAGS}"
;;
--version-*)
CUSTOM_VERSION_KEY=$(echo $1 | sed -e 's/^--version-//g;s/=.*$//g')
CUSTOM_VERSION_VALUE=$(echo $1 | sed -e 's/^--version-.*=//g')
echo -e "INFO: Custom version detected: ${CUSTOM_VERSION_KEY} ${CUSTOM_VERSION_VALUE}\n" 1>>"${BASEDIR}"/build.log 2>&1
generate_custom_version_environment_variables "${CUSTOM_VERSION_KEY}" "${CUSTOM_VERSION_VALUE}"
;;
*)
print_unknown_option "$1"
;;
@ -150,6 +165,12 @@ if [[ -z ${BUILD_VERSION} ]]; then
exit 1
fi
if [[ -z ${ANDROID_TOOLCHAIN} ]]; then
export ANDROID_TOOLCHAIN="${ANDROID_NDK_ROOT}"/toolchains/llvm/prebuilt/"$(get_toolchain)"
fi
echo -e "INFO: Using Android toolchain at ${ANDROID_TOOLCHAIN}\n" 1>>"${BASEDIR}"/build.log 2>&1
# PROCESS FULL OPTION AS LAST OPTION
if [[ -n ${BUILD_FULL} ]]; then
for library in {0..61}; do
@ -164,7 +185,7 @@ if [[ -n ${BUILD_FULL} ]]; then
fi
# DISABLE SPECIFIED LIBRARIES
for disabled_library in ${disabled_libraries[@]}; do
for disabled_library in "${disabled_libraries[@]}"; do
set_library "${disabled_library}" 0
done
@ -175,7 +196,7 @@ if [[ -n ${DISPLAY_HELP} ]]; then
fi
# SET API LEVEL IN build.gradle
${SED_INLINE} "s/minSdkVersion .*/minSdkVersion ${API}/g" "${BASEDIR}"/android/ffmpeg-kit-android-lib/build.gradle 1>>"${BASEDIR}"/build.log 2>&1
${SED_INLINE} "s/minSdk .*/minSdk ${API}/g" "${BASEDIR}"/android/ffmpeg-kit-android-lib/build.gradle 1>>"${BASEDIR}"/build.log 2>&1
${SED_INLINE} "s/versionCode ..0/versionCode ${API}0/g" "${BASEDIR}"/android/ffmpeg-kit-android-lib/build.gradle 1>>"${BASEDIR}"/build.log 2>&1
echo -e "\nBuilding ffmpeg-kit ${BUILD_TYPE_ID}library for Android\n"
@ -203,6 +224,7 @@ for gpl_library in {$LIBRARY_X264,$LIBRARY_XVIDCORE,$LIBRARY_X265,$LIBRARY_LIBVI
fi
done
trap fail_operation EXIT
echo -n -e "\nDownloading sources: "
echo -e "INFO: Downloading the source code of ffmpeg and external libraries.\n" 1>>"${BASEDIR}"/build.log 2>&1
@ -227,8 +249,6 @@ for run_arch in {0..12}; do
fi
export ARCH=$(get_arch_name $run_arch)
export TOOLCHAIN=$(get_toolchain)
export TOOLCHAIN_ARCH=$(get_toolchain_arch)
# EXECUTE MAIN BUILD SCRIPT
. "${BASEDIR}"/scripts/main-android.sh "${ENABLED_LIBRARIES[@]}" || exit 1
@ -246,20 +266,22 @@ done
export API=${ORIGINAL_API}
# SET ARCHITECTURES TO BUILD
rm -f "${BASEDIR}"/android/build/.armv7 1>>"${BASEDIR}"/build.log 2>&1
rm -f "${BASEDIR}"/android/build/.armv7neon 1>>"${BASEDIR}"/build.log 2>&1
rm -f "${BASEDIR}"/android/build/.lts 1>>"${BASEDIR}"/build.log 2>&1
create_file "${BASEDIR}"/android/jni/build.mk "API := ${API}"
ANDROID_ARCHITECTURES=""
if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A]} -eq 1 ]] || [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A_NEON]} -eq 1 ]]; then
ANDROID_ARCHITECTURES+="$(get_android_arch 0) "
fi
if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A]} -eq 1 ]]; then
mkdir -p "${BASEDIR}"/android/build 1>>"${BASEDIR}"/build.log 2>&1
create_file "${BASEDIR}"/android/build/.armv7
append_file "${BASEDIR}"/android/jni/build.mk "ARMV7 := true"
else
append_file "${BASEDIR}"/android/jni/build.mk "ARMV7 := false"
fi
if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A_NEON]} -eq 1 ]]; then
mkdir -p "${BASEDIR}"/android/build 1>>"${BASEDIR}"/build.log 2>&1
create_file "${BASEDIR}"/android/build/.armv7neon
append_file "${BASEDIR}"/android/jni/build.mk "ARMV7_NEON := true"
else
append_file "${BASEDIR}"/android/jni/build.mk "ARMV7_NEON := false"
fi
if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM64_V8A]} -eq 1 ]]; then
ANDROID_ARCHITECTURES+="$(get_android_arch 2) "
@ -270,9 +292,15 @@ fi
if [[ ${ENABLED_ARCHITECTURES[ARCH_X86_64]} -eq 1 ]]; then
ANDROID_ARCHITECTURES+="$(get_android_arch 4) "
fi
if [[ ! -z ${FFMPEG_KIT_LTS_BUILD} ]]; then
mkdir -p "${BASEDIR}"/android/build 1>>"${BASEDIR}"/build.log 2>&1
create_file "${BASEDIR}"/android/build/.lts
append_file "${BASEDIR}"/android/jni/build.mk "ARMV7_BUILD_PATH := android-arm-${API}"
append_file "${BASEDIR}"/android/jni/build.mk "ARMV7_NEON_BUILD_PATH := android-arm-neon-${API}"
append_file "${BASEDIR}"/android/jni/build.mk "X86_BUILD_PATH := android-x86-${API}"
if [[ $(compare_versions "$API" "21") -lt 0 ]]; then
append_file "${BASEDIR}"/android/jni/build.mk "ARM64_BUILD_PATH := android-arm64-21"
append_file "${BASEDIR}"/android/jni/build.mk "X86_64_BUILD_PATH := android-x86_64-21"
else
append_file "${BASEDIR}"/android/jni/build.mk "ARM64_BUILD_PATH := android-arm64-${API}"
append_file "${BASEDIR}"/android/jni/build.mk "X86_64_BUILD_PATH := android-x86_64-${API}"
fi
# BUILD FFMPEG-KIT
@ -300,8 +328,7 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
RC=$(copy_external_library_license_file ${library} "${LICENSE_FILE}")
if [[ ${RC} -ne 0 ]]; then
echo -e "DEBUG: Failed to copy the license file of ${ENABLED_LIBRARY}\n" 1>>"${BASEDIR}"/build.log 2>&1
echo -e "failed\n\nSee build.log for details\n"
echo -e "ERROR: Failed to copy the license file of ${ENABLED_LIBRARY}\n" 1>>"${BASEDIR}"/build.log 2>&1
exit 1
fi
@ -321,8 +348,7 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
RC=$?
if [[ ${RC} -ne 0 ]]; then
echo -e "DEBUG: Failed to copy the license file of custom library ${!library_name}\n" 1>>"${BASEDIR}"/build.log 2>&1
echo -e "failed\n\nSee build.log for details\n"
echo -e "ERROR: Failed to copy the license file of custom library ${!library_name}\n" 1>>"${BASEDIR}"/build.log 2>&1
exit 1
fi
@ -344,7 +370,9 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
# BUILD NATIVE LIBRARY
if [[ ${SKIP_ffmpeg_kit} -ne 1 ]]; then
if [ "$(is_darwin_arm64)" == "1" ]; then
# NDK >= 24.0 DOES NOT REQUIRE arch -x86_64 ON DARWIN ARM64 HOSTS
if [[ "$(is_darwin_arm64)" == "1" ]] && [[ $(compare_versions "$DETECTED_NDK_VERSION" "24.0") -lt 1 ]]; then
arch -x86_64 "${ANDROID_NDK_ROOT}"/ndk-build -B 1>>"${BASEDIR}"/build.log 2>&1
else
"${ANDROID_NDK_ROOT}"/ndk-build -B 1>>"${BASEDIR}"/build.log 2>&1
@ -353,7 +381,6 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
if [ $? -eq 0 ]; then
echo "ok"
else
echo "failed"
exit 1
fi
else
@ -371,7 +398,6 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
rm -f "${BASEDIR}"/android/ffmpeg-kit-android-lib/build/outputs/aar/ffmpeg-kit-release.aar 1>>"${BASEDIR}"/build.log 2>&1
./gradlew ffmpeg-kit-android-lib:clean ffmpeg-kit-android-lib:assembleRelease ffmpeg-kit-android-lib:testReleaseUnitTest 1>>"${BASEDIR}"/build.log 2>&1
if [ $? -ne 0 ]; then
echo -e "failed\n"
exit 1
fi
@ -381,7 +407,6 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
mkdir -p "${FFMPEG_KIT_AAR}" 1>>"${BASEDIR}"/build.log 2>&1
cp "${BASEDIR}"/android/ffmpeg-kit-android-lib/build/outputs/aar/ffmpeg-kit-release.aar "${FFMPEG_KIT_AAR}"/ffmpeg-kit.aar 1>>"${BASEDIR}"/build.log 2>&1
if [ $? -ne 0 ]; then
echo -e "failed\n"
exit 1
fi

@ -4,7 +4,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.0'
classpath 'com.android.tools.build:gradle:8.5.2'
}
}

@ -38,7 +38,7 @@ PROJECT_NAME = "FFmpegKit Android API"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 6.0
PROJECT_NUMBER = 6.1.2
# 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

@ -4,14 +4,13 @@ android {
if (project.android.hasProperty("namespace")) {
namespace 'com.arthenica.ffmpegkit'
}
compileSdk 33
ndkVersion "22.1.7171670"
compileSdk 34
defaultConfig {
minSdk 24
targetSdk 33
versionCode 240600
versionName "6.0"
targetSdk 34
versionCode 240612
versionName "6.1.2"
project.archivesBaseName = "ffmpeg-kit"
consumerProguardFiles "consumer-rules.pro"
}
@ -60,6 +59,6 @@ task javadoc(type: Javadoc) {
dependencies {
api 'com.arthenica:smart-exception-java:0.2.1'
testImplementation "androidx.test.ext:junit:1.1.5"
testImplementation 'org.json:json:20230618'
testImplementation "androidx.test.ext:junit:1.2.1"
testImplementation 'org.json:json:20240303'
}

@ -1,2 +1 @@
/android_lts_support.o
/libandroidltssupport.a
/*.o

@ -26,44 +26,50 @@
* SUCH DAMAGE.
*/
#include <math.h>
#include <errno.h>
#include <malloc.h>
#include <math.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
// posix_memalign() and memalign() are not available in the NDK
// __INTRODUCED_IN(17)
#if __ANDROID_API__ < 17
int posix_memalign(void** memptr, size_t alignment, size_t size) {
if ((alignment & (alignment - 1)) != 0 || alignment == 0) {
return EINVAL;
}
int posix_memalign(void **memptr, size_t alignment, size_t size) {
if ((alignment & (alignment - 1)) != 0 || alignment == 0) {
return EINVAL;
}
if (alignment % sizeof(void*) != 0) {
return EINVAL;
}
if (alignment % sizeof(void *) != 0) {
return EINVAL;
}
*memptr = memalign(alignment, size);
if (*memptr == NULL) {
return errno;
}
*memptr = memalign(alignment, size);
if (*memptr == NULL) {
return errno;
}
return 0;
return 0;
}
#endif /* __ANDROID_API__ < 17 */
double log2(double x) {
return (log(x) / M_LN2);
}
// log2() and log2f() are C99 functions, but they're not available in the NDK
// __INTRODUCED_IN(18)
float log2f(float x) {
return (float) log2((double) x);
}
#if __ANDROID_API__ < 18
double log2(double x) { return (log(x) / M_LN2); }
float log2f(float x) { return (float)log2((double)x); }
#endif /* __ANDROID_API__ < 18 */
#ifdef __cplusplus
}; /* end of extern "C" */
}; /* end of extern "C" */
#endif

@ -0,0 +1,182 @@
#include "ffmpeg_context.h"
FFmpegContext *saveFFmpegContext() {
FFmpegContext *context = (FFmpegContext *)av_mallocz(sizeof(FFmpegContext));
// cmdutils.c
context->sws_dict = sws_dict;
context->swr_opts = swr_opts;
context->format_opts = format_opts;
context->codec_opts = codec_opts;
context->hide_banner = hide_banner;
#if HAVE_COMMANDLINETOARGVW && defined(_WIN32)
/* Will be leaked on exit */
context->win32_argv_utf8 = win32_argv_utf8;
context->win32_argc = win32_argc;
#endif
// ffmpeg.c
context->vstats_file = vstats_file;
context->nb_output_dumped = nb_output_dumped;
context->current_time = current_time;
context->progress_avio = progress_avio;
context->input_files = input_files;
context->nb_input_files = nb_input_files;
context->output_files = output_files;
context->nb_output_files = nb_output_files;
context->filtergraphs = filtergraphs;
context->nb_filtergraphs = nb_filtergraphs;
#if HAVE_TERMIOS_H
/* init terminal so that we can grab keys */
context->oldtty = oldtty;
context->restore_tty = restore_tty;
#endif
context->received_sigterm = received_sigterm;
context->received_nb_signals = received_nb_signals;
context->transcode_init_done = transcode_init_done;
context->ffmpeg_exited = ffmpeg_exited;
context->copy_ts_first_pts = copy_ts_first_pts;
// ffmpeg_hw.c
context->nb_hw_devices = nb_hw_devices;
context->hw_devices = hw_devices;
// ffmpeg_mux.c
context->want_sdp = want_sdp;
// ffmpeg_mux_init.c
context->enc_stats_files = enc_stats_files;
context->nb_enc_stats_files = nb_enc_stats_files;
// ffmpeg_opt.c
context->filter_hw_device = filter_hw_device;
context->vstats_filename = vstats_filename;
context->sdp_filename = sdp_filename;
context->audio_drift_threshold = audio_drift_threshold;
context->dts_delta_threshold = dts_delta_threshold;
context->dts_error_threshold = dts_error_threshold;
context->video_sync_method = video_sync_method;
context->frame_drop_threshold = frame_drop_threshold;
context->do_benchmark = do_benchmark;
context->do_benchmark_all = do_benchmark_all;
context->do_hex_dump = do_hex_dump;
context->do_pkt_dump = do_pkt_dump;
context->copy_ts = copy_ts;
context->start_at_zero = start_at_zero;
context->copy_tb = copy_tb;
context->debug_ts = debug_ts;
context->exit_on_error = exit_on_error;
context->abort_on_flags = abort_on_flags;
context->print_stats = print_stats;
context->stdin_interaction = stdin_interaction;
context->max_error_rate = max_error_rate;
context->filter_nbthreads = filter_nbthreads;
context->filter_complex_nbthreads = filter_complex_nbthreads;
context->vstats_version = vstats_version;
context->auto_conversion_filters = auto_conversion_filters;
context->stats_period = stats_period;
context->file_overwrite = file_overwrite;
context->no_file_overwrite = no_file_overwrite;
#if FFMPEG_OPT_PSNR
context->do_psnr = do_psnr;
#endif
context->ignore_unknown_streams = ignore_unknown_streams;
context->copy_unknown_streams = copy_unknown_streams;
context->recast_media = recast_media;
// opt_common.c
context->report_file = report_file;
context->report_file_level = report_file_level;
context->warned_cfg = warned_cfg;
return context;
}
void loadFFmpegContext(FFmpegContext *context) {
// cmdutils.c
sws_dict = context->sws_dict;
swr_opts = context->swr_opts;
format_opts = context->format_opts;
codec_opts = context->codec_opts;
hide_banner = context->hide_banner;
#if HAVE_COMMANDLINETOARGVW && defined(_WIN32)
/* Will be leaked on exit */
win32_argv_utf8 = context->win32_argv_utf8;
win32_argc = context->win32_argc;
#endif
// ffmpeg.c
vstats_file = context->vstats_file;
nb_output_dumped = context->nb_output_dumped;
current_time = context->current_time;
progress_avio = context->progress_avio;
input_files = context->input_files;
nb_input_files = context->nb_input_files;
output_files = context->output_files;
nb_output_files = context->nb_output_files;
filtergraphs = context->filtergraphs;
nb_filtergraphs = context->nb_filtergraphs;
#if HAVE_TERMIOS_H
/* init terminal so that we can grab keys */
oldtty = context->oldtty;
restore_tty = context->restore_tty;
#endif
received_sigterm = context->received_sigterm;
received_nb_signals = context->received_nb_signals;
transcode_init_done = context->transcode_init_done;
ffmpeg_exited = context->ffmpeg_exited;
copy_ts_first_pts = context->copy_ts_first_pts;
// ffmpeg_hw.c
nb_hw_devices = context->nb_hw_devices;
hw_devices = context->hw_devices;
// ffmpeg_mux.c
want_sdp = context->want_sdp;
// ffmpeg_mux_init.c
enc_stats_files = context->enc_stats_files;
nb_enc_stats_files = context->nb_enc_stats_files;
// ffmpeg_opt.c
filter_hw_device = context->filter_hw_device;
vstats_filename = context->vstats_filename;
sdp_filename = context->sdp_filename;
audio_drift_threshold = context->audio_drift_threshold;
dts_delta_threshold = context->dts_delta_threshold;
dts_error_threshold = context->dts_error_threshold;
video_sync_method = context->video_sync_method;
frame_drop_threshold = context->frame_drop_threshold;
do_benchmark = context->do_benchmark;
do_benchmark_all = context->do_benchmark_all;
do_hex_dump = context->do_hex_dump;
do_pkt_dump = context->do_pkt_dump;
copy_ts = context->copy_ts;
start_at_zero = context->start_at_zero;
copy_tb = context->copy_tb;
debug_ts = context->debug_ts;
exit_on_error = context->exit_on_error;
abort_on_flags = context->abort_on_flags;
print_stats = context->print_stats;
stdin_interaction = context->stdin_interaction;
max_error_rate = context->max_error_rate;
filter_nbthreads = context->filter_nbthreads;
filter_complex_nbthreads = context->filter_complex_nbthreads;
vstats_version = context->vstats_version;
auto_conversion_filters = context->auto_conversion_filters;
stats_period = context->stats_period;
file_overwrite = context->file_overwrite;
no_file_overwrite = context->no_file_overwrite;
#if FFMPEG_OPT_PSNR
do_psnr = context->do_psnr;
#endif
ignore_unknown_streams = context->ignore_unknown_streams;
copy_unknown_streams = context->copy_unknown_streams;
recast_media = context->recast_media;
// opt_common.c
report_file = context->report_file;
report_file_level = context->report_file_level;
warned_cfg = context->warned_cfg;
}

@ -0,0 +1,144 @@
/*
* Copyright (c) 2024 ARTHENICA LTD
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FFMPEG_CONTEXT_H
#define FFMPEG_CONTEXT_H
#include "fftools_ffmpeg.h"
#include "libavformat/avio.h"
#include "libavutil/dict.h"
extern __thread BenchmarkTimeStamps current_time;
#if HAVE_TERMIOS_H
#include <termios.h>
extern __thread struct termios oldtty;
#endif
extern __thread int restore_tty;
extern __thread volatile int received_sigterm;
extern __thread volatile int received_nb_signals;
extern __thread atomic_int transcode_init_done;
extern __thread volatile int ffmpeg_exited;
extern __thread int64_t copy_ts_first_pts;
extern __thread int nb_hw_devices;
extern __thread HWDevice **hw_devices;
extern __thread int want_sdp;
extern __thread struct EncStatsFile *enc_stats_files;
extern __thread int nb_enc_stats_files;
extern __thread float audio_drift_threshold;
extern __thread int file_overwrite;
extern __thread int no_file_overwrite;
extern __thread FILE *report_file;
extern __thread int report_file_level;
extern __thread int warned_cfg;
typedef struct FFmpegContext {
// cmdutils.c
AVDictionary *sws_dict;
AVDictionary *swr_opts;
AVDictionary *format_opts, *codec_opts;
int hide_banner;
#if HAVE_COMMANDLINETOARGVW && defined(_WIN32)
/* Will be leaked on exit */
char **win32_argv_utf8;
int win32_argc;
#endif
// ffmpeg.c
FILE *vstats_file;
unsigned nb_output_dumped;
BenchmarkTimeStamps current_time;
AVIOContext *progress_avio;
InputFile **input_files;
int nb_input_files;
OutputFile **output_files;
int nb_output_files;
FilterGraph **filtergraphs;
int nb_filtergraphs;
#if HAVE_TERMIOS_H
/* init terminal so that we can grab keys */
struct termios oldtty;
int restore_tty;
#endif
volatile int received_sigterm;
volatile int received_nb_signals;
atomic_int transcode_init_done;
volatile int ffmpeg_exited;
int64_t copy_ts_first_pts;
// ffmpeg_hw.c
int nb_hw_devices;
HWDevice **hw_devices;
// ffmpeg_mux.c
int want_sdp;
// ffmpeg_mux_init.c
EncStatsFile *enc_stats_files;
int nb_enc_stats_files;
// ffmpeg_opt.c
HWDevice *filter_hw_device;
char *vstats_filename;
char *sdp_filename;
float audio_drift_threshold;
float dts_delta_threshold;
float dts_error_threshold;
enum VideoSyncMethod video_sync_method;
float frame_drop_threshold;
int do_benchmark;
int do_benchmark_all;
int do_hex_dump;
int do_pkt_dump;
int copy_ts;
int start_at_zero;
int copy_tb;
int debug_ts;
int exit_on_error;
int abort_on_flags;
int print_stats;
int stdin_interaction;
float max_error_rate;
char *filter_nbthreads;
int filter_complex_nbthreads;
int vstats_version;
int auto_conversion_filters;
int64_t stats_period;
int file_overwrite;
int no_file_overwrite;
#if FFMPEG_OPT_PSNR
int do_psnr;
#endif
int ignore_unknown_streams;
int copy_unknown_streams;
int recast_media;
// opt_common.c
FILE *report_file;
int report_file_level;
int warned_cfg;
void *arg;
} FFmpegContext;
FFmpegContext *saveFFmpegContext();
void loadFFmpegContext(FFmpegContext *context);
#endif // FFMPEG_CONTEXT_H

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2021 Taner Sener
* Copyright (c) 2024 ARTHENICA LTD
*
* This file is part of FFmpegKit.
*
@ -19,37 +20,37 @@
#include <pthread.h>
#include <stdatomic.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "config.h"
#include "ffmpegkit.h"
#include "ffprobekit.h"
#include "fftools_ffmpeg.h"
#include "libavcodec/jni.h"
#include "libavutil/bprint.h"
#include "libavutil/file.h"
#include "fftools_ffmpeg.h"
#include "ffmpegkit.h"
#include "ffprobekit.h"
# define LogType 1
# define StatisticsType 2
#define LogType 1
#define StatisticsType 2
/** Callback data structure */
struct CallbackData {
int type; // 1 (log callback) or 2 (statistics callback)
long sessionId; // session identifier
int type; // 1 (log callback) or 2 (statistics callback)
long sessionId; // session identifier
int logLevel; // log level
AVBPrint logData; // log data
int logLevel; // log level
AVBPrint logData; // log data
int statisticsFrameNumber; // statistics frame number
float statisticsFps; // statistics fps
float statisticsQuality; // statistics quality
int64_t statisticsSize; // statistics size
double statisticsTime; // statistics time
double statisticsBitrate; // statistics bitrate
double statisticsSpeed; // statistics speed
int statisticsFrameNumber; // statistics frame number
float statisticsFps; // statistics fps
float statisticsQuality; // statistics quality
int64_t statisticsSize; // statistics size
double statisticsTime; // statistics time
double statisticsBitrate; // statistics bitrate
double statisticsSpeed; // statistics speed
struct CallbackData *next;
struct CallbackData *next;
};
/** Session control variables */
@ -113,21 +114,41 @@ int configuredLogLevel = AV_LOG_INFO;
/** Prototypes of native functions defined by Config class. */
JNINativeMethod configMethods[] = {
{"enableNativeRedirection", "()V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection},
{"disableNativeRedirection", "()V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_disableNativeRedirection},
{"setNativeLogLevel", "(I)V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLogLevel},
{"getNativeLogLevel", "()I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLogLevel},
{"getNativeFFmpegVersion", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeFFmpegVersion},
{"getNativeVersion", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeVersion},
{"nativeFFmpegExecute", "(J[Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegExecute},
{"nativeFFmpegCancel", "(J)V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegCancel},
{"nativeFFprobeExecute", "(J[Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute},
{"registerNewNativeFFmpegPipe", "(Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNativeFFmpegPipe},
{"getNativeBuildDate", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeBuildDate},
{"setNativeEnvironmentVariable", "(Ljava/lang/String;Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvironmentVariable},
{"ignoreNativeSignal", "(I)V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNativeSignal},
{"messagesInTransmit", "(J)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit}
};
{"enableNativeRedirection", "()V",
(void *)
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection},
{"disableNativeRedirection", "()V",
(void *)
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_disableNativeRedirection},
{"setNativeLogLevel", "(I)V",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLogLevel},
{"getNativeLogLevel", "()I",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLogLevel},
{"getNativeFFmpegVersion", "()Ljava/lang/String;",
(void *)
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeFFmpegVersion},
{"getNativeVersion", "()Ljava/lang/String;",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeVersion},
{"getNativePackageName", "()Ljava/lang/String;",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativePackageName},
{"nativeFFmpegExecute", "(J[Ljava/lang/String;)I",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegExecute},
{"nativeFFmpegCancel", "(J)V",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegCancel},
{"nativeFFprobeExecute", "(J[Ljava/lang/String;)I",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute},
{"registerNewNativeFFmpegPipe", "(Ljava/lang/String;)I",
(void *)
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNativeFFmpegPipe},
{"getNativeBuildDate", "()Ljava/lang/String;",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeBuildDate},
{"setNativeEnvironmentVariable", "(Ljava/lang/String;Ljava/lang/String;)I",
(void *)
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvironmentVariable},
{"ignoreNativeSignal", "(I)V",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNativeSignal},
{"messagesInTransmit", "(J)I",
(void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit}};
/** Forward declaration for function defined in fftools_ffmpeg.c */
int ffmpeg_execute(int argc, char **argv);
@ -157,42 +178,45 @@ static const char *avutil_log_get_level_str(int level) {
}
}
static void avutil_log_format_line(void *avcl, int level, const char *fmt, va_list vl, AVBPrint part[4], int *print_prefix) {
static void avutil_log_format_line(void *avcl, int level, const char *fmt,
va_list vl, AVBPrint part[4],
int *print_prefix) {
int flags = av_log_get_flags();
AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
av_bprint_init(part+0, 0, 1);
av_bprint_init(part+1, 0, 1);
av_bprint_init(part+2, 0, 1);
av_bprint_init(part+3, 0, 65536);
AVClass *avc = avcl ? *(AVClass **)avcl : NULL;
av_bprint_init(part + 0, 0, 1);
av_bprint_init(part + 1, 0, 1);
av_bprint_init(part + 2, 0, 1);
av_bprint_init(part + 3, 0, 65536);
if (*print_prefix && avc) {
if (avc->parent_log_context_offset) {
AVClass** parent = *(AVClass ***) (((uint8_t *) avcl) +
avc->parent_log_context_offset);
AVClass **parent = *(AVClass ***)(((uint8_t *)avcl) +
avc->parent_log_context_offset);
if (parent && *parent) {
av_bprintf(part+0, "[%s @ %p] ",
(*parent)->item_name(parent), parent);
av_bprintf(part + 0, "[%s @ %p] ", (*parent)->item_name(parent),
parent);
}
}
av_bprintf(part+1, "[%s @ %p] ",
avc->item_name(avcl), avcl);
av_bprintf(part + 1, "[%s @ %p] ", avc->item_name(avcl), avcl);
}
if (*print_prefix && (level > AV_LOG_QUIET) && (flags & AV_LOG_PRINT_LEVEL))
av_bprintf(part+2, "[%s] ", avutil_log_get_level_str(level));
av_bprintf(part + 2, "[%s] ", avutil_log_get_level_str(level));
av_vbprintf(part+3, fmt, vl);
av_vbprintf(part + 3, fmt, vl);
if(*part[0].str || *part[1].str || *part[2].str || *part[3].str) {
char lastc = part[3].len && part[3].len <= part[3].size ? part[3].str[part[3].len - 1] : 0;
if (*part[0].str || *part[1].str || *part[2].str || *part[3].str) {
char lastc = part[3].len && part[3].len <= part[3].size
? part[3].str[part[3].len - 1]
: 0;
*print_prefix = lastc == '\n' || lastc == '\r';
}
}
static void avutil_log_sanitize(uint8_t *line) {
while(*line){
if(*line < 0x08 || (*line > 0x0D && *line < 0x20))
*line='?';
while (*line) {
if (*line < 0x08 || (*line > 0x0D && *line < 0x20))
*line = '?';
line++;
}
}
@ -222,22 +246,16 @@ void monitorInit() {
pthread_condattr_destroy(&cattributes);
}
void mutexUnInit() {
pthread_mutex_destroy(&lockMutex);
}
void mutexUnInit() { pthread_mutex_destroy(&lockMutex); }
void monitorUnInit() {
pthread_mutex_destroy(&monitorMutex);
pthread_cond_destroy(&monitorCondition);
}
void mutexLock() {
pthread_mutex_lock(&lockMutex);
}
void mutexLock() { pthread_mutex_lock(&lockMutex); }
void mutexUnlock() {
pthread_mutex_unlock(&lockMutex);
}
void mutexUnlock() { pthread_mutex_unlock(&lockMutex); }
void monitorWait(int milliSeconds) {
struct timeval tp;
@ -249,10 +267,10 @@ void monitorWait(int milliSeconds) {
return;
}
ts.tv_sec = tp.tv_sec;
ts.tv_sec = tp.tv_sec;
ts.tv_nsec = tp.tv_usec * 1000;
ts.tv_sec += milliSeconds / 1000;
ts.tv_nsec += (milliSeconds % 1000)*1000000;
ts.tv_nsec += (milliSeconds % 1000) * 1000000;
ts.tv_sec += ts.tv_nsec / 1000000000L;
ts.tv_nsec = ts.tv_nsec % 1000000000L;
@ -276,7 +294,8 @@ void monitorNotify() {
void logCallbackDataAdd(int level, AVBPrint *data) {
// CREATE DATA STRUCT FIRST
struct CallbackData *newData = (struct CallbackData*)av_malloc(sizeof(struct CallbackData));
struct CallbackData *newData =
(struct CallbackData *)av_malloc(sizeof(struct CallbackData));
newData->type = LogType;
newData->sessionId = globalSessionId;
newData->logLevel = level;
@ -291,7 +310,8 @@ void logCallbackDataAdd(int level, AVBPrint *data) {
callbackDataTail = newData;
if (callbackDataHead != NULL) {
LOGE("Dangling callback data head detected. This can cause memory leak.");
LOGE("Dangling callback data head detected. This can cause memory "
"leak.");
} else {
callbackDataHead = newData;
}
@ -306,16 +326,21 @@ void logCallbackDataAdd(int level, AVBPrint *data) {
monitorNotify();
atomic_fetch_add(&sessionInTransitMessageCountMap[globalSessionId % SESSION_MAP_SIZE], 1);
atomic_fetch_add(
&sessionInTransitMessageCountMap[globalSessionId % SESSION_MAP_SIZE],
1);
}
/**
* Adds statistics data to the end of callback data list.
*/
void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_t size, double time, double bitrate, double speed) {
void statisticsCallbackDataAdd(int frameNumber, float fps, float quality,
int64_t size, double time, double bitrate,
double speed) {
// CREATE DATA STRUCT FIRST
struct CallbackData *newData = (struct CallbackData*)av_malloc(sizeof(struct CallbackData));
struct CallbackData *newData =
(struct CallbackData *)av_malloc(sizeof(struct CallbackData));
newData->type = StatisticsType;
newData->sessionId = globalSessionId;
newData->statisticsFrameNumber = frameNumber;
@ -335,7 +360,8 @@ void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_
callbackDataTail = newData;
if (callbackDataHead != NULL) {
LOGE("Dangling callback data head detected. This can cause memory leak.");
LOGE("Dangling callback data head detected. This can cause memory "
"leak.");
} else {
callbackDataHead = newData;
}
@ -350,7 +376,9 @@ void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_
monitorNotify();
atomic_fetch_add(&sessionInTransitMessageCountMap[globalSessionId % SESSION_MAP_SIZE], 1);
atomic_fetch_add(
&sessionInTransitMessageCountMap[globalSessionId % SESSION_MAP_SIZE],
1);
}
/**
@ -378,7 +406,9 @@ struct CallbackData *callbackDataRemove() {
struct CallbackData *nextHead = currentData->next;
if (nextHead == NULL) {
if (callbackDataHead != callbackDataTail) {
LOGE("Head and tail callback data pointers do not match for single callback data element. This can cause memory leak.");
LOGE("Head and tail callback data pointers do not match for "
"single callback data element. This can cause memory "
"leak.");
} else {
callbackDataTail = NULL;
}
@ -413,7 +443,8 @@ void cancelSession(long id) {
}
/**
* Checks whether a cancel request for the given session id exists in the session map.
* Checks whether a cancel request for the given session id exists in the
* session map.
*
* @param id session id
* @return 1 if exists, false otherwise
@ -443,7 +474,8 @@ void resetMessagesInTransmit(long id) {
* @param format format string
* @param vargs arguments
*/
void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, va_list vargs) {
void ffmpegkit_log_callback_function(void *ptr, int level, const char *format,
va_list vargs) {
AVBPrint fullLine;
AVBPrint part[4];
int print_prefix = 1;
@ -454,7 +486,8 @@ void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, v
int activeLogLevel = av_log_get_level();
// AV_LOG_STDERR logs are always redirected
if ((activeLogLevel == AV_LOG_QUIET && level != AV_LOG_STDERR) || (level > activeLogLevel)) {
if ((activeLogLevel == AV_LOG_QUIET && level != AV_LOG_STDERR) ||
(level > activeLogLevel)) {
return;
}
@ -467,16 +500,17 @@ void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, v
avutil_log_sanitize(part[3].str);
// COMBINE ALL 4 LOG PARTS
av_bprintf(&fullLine, "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str);
av_bprintf(&fullLine, "%s%s%s%s", part[0].str, part[1].str, part[2].str,
part[3].str);
if (fullLine.len > 0) {
logCallbackDataAdd(level, &fullLine);
}
av_bprint_finalize(part, NULL);
av_bprint_finalize(part+1, NULL);
av_bprint_finalize(part+2, NULL);
av_bprint_finalize(part+3, NULL);
av_bprint_finalize(part + 1, NULL);
av_bprint_finalize(part + 2, NULL);
av_bprint_finalize(part + 3, NULL);
av_bprint_finalize(&fullLine, NULL);
}
@ -491,8 +525,12 @@ void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, v
* @param bitrate output bit rate in kbits/s
* @param speed processing speed = processed duration / operation duration
*/
void ffmpegkit_statistics_callback_function(int frameNumber, float fps, float quality, int64_t size, double time, double bitrate, double speed) {
statisticsCallbackDataAdd(frameNumber, fps, quality, size, time, bitrate, speed);
void ffmpegkit_statistics_callback_function(int frameNumber, float fps,
float quality, int64_t size,
double time, double bitrate,
double speed) {
statisticsCallbackDataAdd(frameNumber, fps, quality, size, time, bitrate,
speed);
}
/**
@ -500,22 +538,26 @@ void ffmpegkit_statistics_callback_function(int frameNumber, float fps, float qu
*/
void *callbackThreadFunction() {
JNIEnv *env;
jint getEnvRc = (*globalVm)->GetEnv(globalVm, (void**) &env, JNI_VERSION_1_6);
jint getEnvRc =
(*globalVm)->GetEnv(globalVm, (void **)&env, JNI_VERSION_1_6);
if (getEnvRc != JNI_OK) {
if (getEnvRc != JNI_EDETACHED) {
LOGE("Callback thread failed to GetEnv for class %s with rc %d.\n", configClassName, getEnvRc);
LOGE("Callback thread failed to GetEnv for class %s with rc %d.\n",
configClassName, getEnvRc);
return NULL;
}
if ((*globalVm)->AttachCurrentThread(globalVm, &env, NULL) != 0) {
LOGE("Callback thread failed to AttachCurrentThread for class %s.\n", configClassName);
LOGE(
"Callback thread failed to AttachCurrentThread for class %s.\n",
configClassName);
return NULL;
}
}
LOGD("Async callback block started.\n");
while(redirectionEnabled) {
while (redirectionEnabled) {
struct CallbackData *callbackData = callbackDataRemove();
if (callbackData != NULL) {
@ -525,9 +567,13 @@ void *callbackThreadFunction() {
int size = callbackData->logData.len;
jbyteArray byteArray = (jbyteArray) (*env)->NewByteArray(env, size);
(*env)->SetByteArrayRegion(env, byteArray, 0, size, callbackData->logData.str);
(*env)->CallStaticVoidMethod(env, configClass, logMethod, (jlong) callbackData->sessionId, callbackData->logLevel, byteArray);
jbyteArray byteArray =
(jbyteArray)(*env)->NewByteArray(env, size);
(*env)->SetByteArrayRegion(env, byteArray, 0, size,
callbackData->logData.str);
(*env)->CallStaticVoidMethod(env, configClass, logMethod,
(jlong)callbackData->sessionId,
callbackData->logLevel, byteArray);
(*env)->DeleteLocalRef(env, byteArray);
// CLEAN LOG DATA
@ -537,15 +583,21 @@ void *callbackThreadFunction() {
// STATISTICS CALLBACK
(*env)->CallStaticVoidMethod(env, configClass, statisticsMethod,
(jlong) callbackData->sessionId, callbackData->statisticsFrameNumber,
callbackData->statisticsFps, callbackData->statisticsQuality,
(*env)->CallStaticVoidMethod(
env, configClass, statisticsMethod,
(jlong)callbackData->sessionId,
callbackData->statisticsFrameNumber,
callbackData->statisticsFps,
callbackData->statisticsQuality,
callbackData->statisticsSize, callbackData->statisticsTime,
callbackData->statisticsBitrate, callbackData->statisticsSpeed);
callbackData->statisticsBitrate,
callbackData->statisticsSpeed);
}
atomic_fetch_sub(&sessionInTransitMessageCountMap[callbackData->sessionId % SESSION_MAP_SIZE], 1);
atomic_fetch_sub(
&sessionInTransitMessageCountMap[callbackData->sessionId %
SESSION_MAP_SIZE],
1);
// CLEAN STRUCT
callbackData->next = NULL;
@ -564,21 +616,68 @@ void *callbackThreadFunction() {
}
/**
* Used by saf protocol; is expected to be called from a Java thread, therefore we don't need attach/detach
* Used by saf protocol; If it is called from a Java thread, we don't need
* attach/detach. However it can be called from other threads as well (as it
* happens for concat demuxer), in that case we perform attach & detach. Returns
* file descriptor created for this SAF id or 0 if an error occurs.
*/
int saf_open(int safId) {
JNIEnv *env = NULL;
(*globalVm)->GetEnv(globalVm, (void**) &env, JNI_VERSION_1_6);
return (*env)->CallStaticIntMethod(env, configClass, safOpenMethod, safId);
bool attached = false;
jint getEnvRc =
(*globalVm)->GetEnv(globalVm, (void **)&env, JNI_VERSION_1_6);
if (getEnvRc != JNI_OK) {
if (getEnvRc != JNI_EDETACHED) {
LOGE("saf_open failed to GetEnv for class %s with rc %d.\n",
configClassName, getEnvRc);
return 0;
}
if ((*globalVm)->AttachCurrentThread(globalVm, &env, NULL) != 0) {
LOGE("saf_open failed to AttachCurrentThread for class %s.\n",
configClassName);
return 0;
} else {
attached = true;
}
}
int result =
(*env)->CallStaticIntMethod(env, configClass, safOpenMethod, safId);
if (attached)
(*globalVm)->DetachCurrentThread(globalVm);
return result;
}
/**
* Used by saf protocol; is expected to be called from a Java thread, therefore we don't need attach/detach
* Used by saf protocol; If it is called from a Java thread, we don't need
* attach/detach. However it can be called from other threads as well (as it
* happens for concat demuxer), in that case we perform attach & detach. Returns
* 1 if the given file descriptor is closed successfully, 0 if an error occurs.
*/
int saf_close(int fd) {
JNIEnv *env = NULL;
(*globalVm)->GetEnv(globalVm, (void**) &env, JNI_VERSION_1_6);
return (*env)->CallStaticIntMethod(env, configClass, safCloseMethod, fd);
bool attached = false;
jint getEnvRc =
(*globalVm)->GetEnv(globalVm, (void **)&env, JNI_VERSION_1_6);
if (getEnvRc != JNI_OK) {
if (getEnvRc != JNI_EDETACHED) {
LOGE("saf_close failed to GetEnv for class %s with rc %d.\n",
configClassName, getEnvRc);
return 0;
}
if ((*globalVm)->AttachCurrentThread(globalVm, &env, NULL) != 0) {
LOGE("saf_close failed to AttachCurrentThread for class %s.\n",
configClassName);
return 0;
} else {
attached = true;
}
}
int result =
(*env)->CallStaticIntMethod(env, configClass, safCloseMethod, fd);
if (attached)
(*globalVm)->DetachCurrentThread(globalVm);
return result;
}
/**
@ -614,7 +713,7 @@ static void enableNativeRedirection() {
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void**)(&env), JNI_VERSION_1_6) != JNI_OK) {
if ((*vm)->GetEnv(vm, (void **)(&env), JNI_VERSION_1_6) != JNI_OK) {
LOGE("OnLoad failed to GetEnv for class %s.\n", configClassName);
return JNI_FALSE;
}
@ -625,8 +724,9 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, localConfigClass, configMethods, 14) < 0) {
LOGE("OnLoad failed to RegisterNatives for class %s.\n", configClassName);
if ((*env)->RegisterNatives(env, localConfigClass, configMethods, 15) < 0) {
LOGE("OnLoad failed to RegisterNatives for class %s.\n",
configClassName);
return JNI_FALSE;
}
@ -638,31 +738,37 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
(*env)->GetJavaVM(env, &globalVm);
logMethod = (*env)->GetStaticMethodID(env, localConfigClass, "log", "(JI[B)V");
logMethod =
(*env)->GetStaticMethodID(env, localConfigClass, "log", "(JI[B)V");
if (logMethod == NULL) {
LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "log");
return JNI_FALSE;
}
statisticsMethod = (*env)->GetStaticMethodID(env, localConfigClass, "statistics", "(JIFFJDDD)V");
statisticsMethod = (*env)->GetStaticMethodID(env, localConfigClass,
"statistics", "(JIFFJDDD)V");
if (statisticsMethod == NULL) {
LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "statistics");
LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n",
"statistics");
return JNI_FALSE;
}
safOpenMethod = (*env)->GetStaticMethodID(env, localConfigClass, "safOpen", "(I)I");
safOpenMethod =
(*env)->GetStaticMethodID(env, localConfigClass, "safOpen", "(I)I");
if (safOpenMethod == NULL) {
LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "safOpen");
return JNI_FALSE;
}
safCloseMethod = (*env)->GetStaticMethodID(env, localConfigClass, "safClose", "(I)I");
safCloseMethod =
(*env)->GetStaticMethodID(env, localConfigClass, "safClose", "(I)I");
if (safCloseMethod == NULL) {
LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "safClose");
return JNI_FALSE;
}
stringConstructor = (*env)->GetMethodID(env, localStringClass, "<init>", "([BLjava/lang/String;)V");
stringConstructor = (*env)->GetMethodID(env, localStringClass, "<init>",
"([BLjava/lang/String;)V");
if (stringConstructor == NULL) {
LOGE("OnLoad thread failed to GetMethodID for %s.\n", "<init>");
return JNI_FALSE;
@ -670,13 +776,13 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
av_jni_set_java_vm(vm, NULL);
configClass = (jclass) ((*env)->NewGlobalRef(env, localConfigClass));
stringClass = (jclass) ((*env)->NewGlobalRef(env, localStringClass));
configClass = (jclass)((*env)->NewGlobalRef(env, localConfigClass));
stringClass = (jclass)((*env)->NewGlobalRef(env, localStringClass));
callbackDataHead = NULL;
callbackDataTail = NULL;
for(int i = 0; i<SESSION_MAP_SIZE; i++) {
for (int i = 0; i < SESSION_MAP_SIZE; i++) {
atomic_init(&sessionMap[i], 0);
atomic_init(&sessionInTransitMessageCountMap[i], 0);
}
@ -686,8 +792,10 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
redirectionEnabled = 0;
#ifdef USES_FFMPEG_KIT_PROTOCOLS
av_set_saf_open(saf_open);
av_set_saf_close(saf_close);
#endif
enableNativeRedirection();
@ -701,7 +809,10 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
* @param object reference to the class on which this method is invoked
* @param level log level
*/
JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLogLevel(JNIEnv *env, jclass object, jint level) {
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLogLevel(JNIEnv *env,
jclass object,
jint level) {
configuredLogLevel = level;
}
@ -711,7 +822,9 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLog
* @param env pointer to native method interface
* @param object reference to the class on which this method is invoked
*/
JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLogLevel(JNIEnv *env, jclass object) {
JNIEXPORT jint JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLogLevel(JNIEnv *env,
jclass object) {
return configuredLogLevel;
}
@ -721,7 +834,9 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLog
* @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_ffmpegkit_FFmpegKitConfig_enableNativeRedirection(JNIEnv *env, jclass object) {
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection(
JNIEnv *env, jclass object) {
enableNativeRedirection();
}
@ -731,7 +846,9 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNative
* @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_ffmpegkit_FFmpegKitConfig_disableNativeRedirection(JNIEnv *env, jclass object) {
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_disableNativeRedirection(
JNIEnv *env, jclass object) {
mutexLock();
@ -756,7 +873,9 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_disableNativ
* @param object reference to the class on which this method is invoked
* @return FFmpeg version string
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeFFmpegVersion(JNIEnv *env, jclass object) {
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeFFmpegVersion(
JNIEnv *env, jclass object) {
return (*env)->NewStringUTF(env, FFMPEG_VERSION);
}
@ -767,10 +886,34 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNative
* @param object reference to the class on which this method is invoked
* @return FFmpegKit version string
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeVersion(JNIEnv *env, jclass object) {
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeVersion(JNIEnv *env,
jclass object) {
return (*env)->NewStringUTF(env, FFMPEG_KIT_VERSION);
}
/**
* Returns the native FFmpegKit package name.
*
* @param env pointer to native method interface
* @param object reference to the class on which this method is invoked
* @return native FFmpegKit package name
*/
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativePackageName(
JNIEnv *env, jclass object) {
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define FFMPEG_KIT_PACKAGE_STR TOSTRING(FFMPEG_KIT_PACKAGE)
#ifdef FFMPEG_KIT_PACKAGE
return (*env)->NewStringUTF(env, FFMPEG_KIT_PACKAGE_STR);
#else
return (*env)->NewStringUTF(env, "");
#endif
}
/**
* Synchronously executes FFmpeg natively with arguments provided.
*
@ -780,7 +923,9 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNative
* @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_ffmpegkit_FFmpegKitConfig_nativeFFmpegExecute(JNIEnv *env, jclass object, jlong id, jobjectArray stringArray) {
JNIEXPORT jint JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegExecute(
JNIEnv *env, jclass object, jlong id, jobjectArray stringArray) {
jstring *tempArray = NULL;
int argumentCount = 1;
char **argv = NULL;
@ -792,30 +937,33 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpeg
int programArgumentCount = (*env)->GetArrayLength(env, stringArray);
argumentCount = programArgumentCount + 1;
tempArray = (jstring *) av_malloc(sizeof(jstring) * programArgumentCount);
tempArray =
(jstring *)av_malloc(sizeof(jstring) * programArgumentCount);
}
/* PRESERVE USAGE FORMAT
*
* ffmpeg <arguments>
*/
argv = (char **)av_malloc(sizeof(char*) * (argumentCount));
argv = (char **)av_malloc(sizeof(char *) * (argumentCount));
argv[0] = (char *)av_malloc(sizeof(char) * (strlen(LIB_NAME) + 1));
strcpy(argv[0], LIB_NAME);
// PREPARE ARRAY ELEMENTS
if (stringArray) {
for (int i = 0; i < (argumentCount - 1); i++) {
tempArray[i] = (jstring) (*env)->GetObjectArrayElement(env, stringArray, i);
tempArray[i] =
(jstring)(*env)->GetObjectArrayElement(env, stringArray, i);
if (tempArray[i] != NULL) {
argv[i + 1] = (char *) (*env)->GetStringUTFChars(env, tempArray[i], 0);
argv[i + 1] =
(char *)(*env)->GetStringUTFChars(env, tempArray[i], 0);
}
}
}
// REGISTER THE ID BEFORE STARTING THE SESSION
globalSessionId = (long) id;
addSession((long) id);
globalSessionId = (long)id;
addSession((long)id);
resetMessagesInTransmit(globalSessionId);
@ -823,7 +971,7 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpeg
int returnCode = ffmpeg_execute(argumentCount, argv);
// ALWAYS REMOVE THE ID FROM THE MAP
removeSession((long) id);
removeSession((long)id);
// CLEANUP
if (tempArray) {
@ -846,7 +994,10 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpeg
* @param object reference to the class on which this method is invoked
* @param id session id
*/
JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegCancel(JNIEnv *env, jclass object, jlong id) {
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegCancel(JNIEnv *env,
jclass object,
jlong id) {
cancel_operation(id);
}
@ -858,8 +1009,11 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpeg
* @param ffmpegPipePath full path of ffmpeg pipe
* @return zero on successful creation, non-zero on error
*/
JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNativeFFmpegPipe(JNIEnv *env, jclass object, jstring ffmpegPipePath) {
const char *ffmpegPipePathString = (*env)->GetStringUTFChars(env, ffmpegPipePath, 0);
JNIEXPORT int JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNativeFFmpegPipe(
JNIEnv *env, jclass object, jstring ffmpegPipePath) {
const char *ffmpegPipePathString =
(*env)->GetStringUTFChars(env, ffmpegPipePath, 0);
return mkfifo(ffmpegPipePathString, S_IRWXU | S_IRWXG | S_IROTH);
}
@ -871,7 +1025,9 @@ JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNa
* @param object reference to the class on which this method is invoked
* @return FFmpegKit library build date
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeBuildDate(JNIEnv *env, jclass object) {
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeBuildDate(JNIEnv *env,
jclass object) {
char buildDate[10];
sprintf(buildDate, "%d", FFMPEG_KIT_BUILD_DATE);
return (*env)->NewStringUTF(env, buildDate);
@ -886,9 +1042,13 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNative
* @param variableValue environment variable value
* @return zero on success, non-zero on error
*/
JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvironmentVariable(JNIEnv *env, jclass object, jstring variableName, jstring variableValue) {
const char *variableNameString = (*env)->GetStringUTFChars(env, variableName, 0);
const char *variableValueString = (*env)->GetStringUTFChars(env, variableValue, 0);
JNIEXPORT int JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvironmentVariable(
JNIEnv *env, jclass object, jstring variableName, jstring variableValue) {
const char *variableNameString =
(*env)->GetStringUTFChars(env, variableName, 0);
const char *variableValueString =
(*env)->GetStringUTFChars(env, variableValue, 0);
int rc = setenv(variableNameString, variableValueString, 1);
@ -898,13 +1058,17 @@ JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvi
}
/**
* Registers a new ignored signal. Ignored signals are not handled by the library.
* Registers a new ignored signal. Ignored signals are not handled by the
* library.
*
* @param env pointer to native method interface
* @param object reference to the class on which this method is invoked
* @param signum signal number
*/
JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNativeSignal(JNIEnv *env, jclass object, jint signum) {
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNativeSignal(JNIEnv *env,
jclass object,
jint signum) {
if (signum == SIGQUIT) {
handleSIGQUIT = 0;
} else if (signum == SIGINT) {
@ -919,13 +1083,16 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNative
}
/**
* Returns the number of native messages which are not transmitted to the Java callbacks for the
* given session.
* Returns the number of native messages which are not transmitted to the Java
* callbacks for the given session.
*
* @param env pointer to native method interface
* @param object reference to the class on which this method is invoked
* @param id session id
*/
JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit(JNIEnv *env, jclass object, jlong id) {
JNIEXPORT int JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit(JNIEnv *env,
jclass object,
jlong id) {
return atomic_load(&sessionInTransitMessageCountMap[id % SESSION_MAP_SIZE]);
}

@ -20,20 +20,22 @@
#ifndef FFMPEG_KIT_H
#define FFMPEG_KIT_H
#include <jni.h>
#include <android/log.h>
#include <jni.h>
#include <string.h>
#include "libavutil/log.h"
#include "libavutil/ffversion.h"
#include "libavutil/log.h"
/** Library version string */
#define FFMPEG_KIT_VERSION "6.0"
#define FFMPEG_KIT_VERSION "6.1.2"
/** Defines tag used for Android logging. */
#define LIB_NAME "ffmpeg-kit"
/** Verbose Android logging macro. */
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LIB_NAME, __VA_ARGS__)
#define LOGV(...) \
__android_log_print(ANDROID_LOG_VERBOSE, LIB_NAME, __VA_ARGS__)
/** Debug Android logging macro. */
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LIB_NAME, __VA_ARGS__)
@ -52,90 +54,127 @@
* Method: enableNativeRedirection
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection(JNIEnv *, jclass);
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection(JNIEnv *,
jclass);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: disableNativeRedirection
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_disableNativeRedirection(JNIEnv *, jclass);
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_disableNativeRedirection(JNIEnv *,
jclass);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: setNativeLogLevel
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLogLevel(JNIEnv *, jclass, jint);
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLogLevel(JNIEnv *, jclass,
jint);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativeLogLevel
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLogLevel(JNIEnv *, jclass);
JNIEXPORT jint JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLogLevel(JNIEnv *,
jclass);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativeFFmpegVersion
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeFFmpegVersion(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeFFmpegVersion(JNIEnv *,
jclass);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativeVersion
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeVersion(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeVersion(JNIEnv *, jclass);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativePackageName
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativePackageName(JNIEnv *,
jclass);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: nativeFFmpegExecute
* Signature: (J[Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegExecute(JNIEnv *, jclass, jlong, jobjectArray);
JNIEXPORT jint JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegExecute(JNIEnv *,
jclass, jlong,
jobjectArray);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: nativeFFmpegCancel
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegCancel(JNIEnv *, jclass, jlong);
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegCancel(JNIEnv *,
jclass, jlong);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: registerNewNativeFFmpegPipe
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNativeFFmpegPipe(JNIEnv *env, jclass object, jstring ffmpegPipePath);
JNIEXPORT int JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNativeFFmpegPipe(
JNIEnv *env, jclass object, jstring ffmpegPipePath);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativeBuildDate
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeBuildDate(JNIEnv *env, jclass object);
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeBuildDate(JNIEnv *env,
jclass object);
/**
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: setNativeEnvironmentVariable
* Signature: (Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvironmentVariable(JNIEnv *env, jclass object, jstring variableName, jstring variableValue);
JNIEXPORT int JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvironmentVariable(
JNIEnv *env, jclass object, jstring variableName, jstring variableValue);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: ignoreNativeSignal
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNativeSignal(JNIEnv *env, jclass object, jint signum);
JNIEXPORT void JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNativeSignal(JNIEnv *env,
jclass object,
jint signum);
/*
* Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: messagesInTransmit
* Signature: (J)I
*/
JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit(JNIEnv *env, jclass object, jlong id);
JNIEXPORT int JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit(JNIEnv *env,
jclass object,
jlong id);
#endif /* FFMPEG_KIT_H */

@ -17,20 +17,25 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ffmpegkit_abidetect.h"
#include "cpu-features.h"
#include "fftools_ffmpeg.h"
#include "ffmpegkit_abidetect.h"
/** Full name of the Java class that owns native functions in this file. */
const char *abiDetectClassName = "com/arthenica/ffmpegkit/AbiDetect";
/** Prototypes of native functions defined by this file. */
JNINativeMethod abiDetectMethods[] = {
{"getNativeAbi", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi},
{"getNativeCpuAbi", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_AbiDetect_getNativeCpuAbi},
{"isNativeLTSBuild", "()Z", (void*) Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild},
{"getNativeBuildConf", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_AbiDetect_getNativeBuildConf}
};
{"getNativeAbi", "()Ljava/lang/String;",
(void *)Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi},
{"getNativeCpuAbi", "()Ljava/lang/String;",
(void *)Java_com_arthenica_ffmpegkit_AbiDetect_getNativeCpuAbi},
{"isNativeLTSBuild", "()Z",
(void *)Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild},
{"getNativeBuildConf", "()Ljava/lang/String;",
(void *)Java_com_arthenica_ffmpegkit_AbiDetect_getNativeBuildConf},
{"getNativeMinSdk", "()Ljava/lang/String;",
(void *)Java_com_arthenica_ffmpegkit_AbiDetect_getNativeMinSdk}};
/**
* Called when 'abidetect' native library is loaded.
@ -41,7 +46,7 @@ JNINativeMethod abiDetectMethods[] = {
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK) {
LOGE("OnLoad failed to GetEnv for class %s.\n", abiDetectClassName);
return JNI_FALSE;
}
@ -52,8 +57,9 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, abiDetectClass, abiDetectMethods, 4) < 0) {
LOGE("OnLoad failed to RegisterNatives for class %s.\n", abiDetectClassName);
if ((*env)->RegisterNatives(env, abiDetectClass, abiDetectMethods, 5) < 0) {
LOGE("OnLoad failed to RegisterNatives for class %s.\n",
abiDetectClassName);
return JNI_FALSE;
}
@ -67,7 +73,8 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
* @param object reference to the class on which this method is invoked
* @return loaded ABI name as UTF string
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi(JNIEnv *env, jclass object) {
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi(
JNIEnv *env, jclass object) {
#ifdef FFMPEG_KIT_ARM_V7A
return (*env)->NewStringUTF(env, "arm-v7a");
@ -80,7 +87,6 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi(JN
#else
return (*env)->NewStringUTF(env, "unknown");
#endif
}
/**
@ -90,7 +96,9 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi(JN
* @param object reference to the class on which this method is invoked
* @return ABI name of the running cpu as UTF string
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeCpuAbi(JNIEnv *env, jclass object) {
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_getNativeCpuAbi(JNIEnv *env,
jclass object) {
AndroidCpuFamily family = android_getCpuFamily();
if (family == ANDROID_CPU_FAMILY_ARM) {
@ -124,12 +132,14 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeCpuAbi
* @param object reference to the class on which this method is invoked
* @return yes or no
*/
JNIEXPORT jboolean JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild(JNIEnv *env, jclass object) {
#if defined(FFMPEG_KIT_LTS)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
JNIEXPORT jboolean JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild(JNIEnv *env,
jclass object) {
#if defined(FFMPEG_KIT_LTS)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
/**
@ -139,6 +149,30 @@ JNIEXPORT jboolean JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBui
* @param object reference to the class on which this method is invoked
* @return build configuration string
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeBuildConf(JNIEnv *env, jclass object) {
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_getNativeBuildConf(JNIEnv *env,
jclass object) {
return (*env)->NewStringUTF(env, FFMPEG_CONFIGURATION);
}
/**
* Returns the minimum Android API level required to run this native library.
*
* @param env pointer to native method interface
* @param object reference to the class on which this method is invoked
* @return the minimum Android API level required to run this native library
*/
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_getNativeMinSdk(
JNIEnv *env, jclass object) {
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define FFMPEG_KIT_MIN_SDK_STR TOSTRING(FFMPEG_KIT_MIN_SDK)
#ifdef FFMPEG_KIT_MIN_SDK
return (*env)->NewStringUTF(env, FFMPEG_KIT_MIN_SDK_STR);
#else
return (*env)->NewStringUTF(env, "");
#endif
}

@ -20,8 +20,8 @@
#ifndef FFMPEG_KIT_ABIDETECT_H
#define FFMPEG_KIT_ABIDETECT_H
#include <jni.h>
#include "ffmpegkit.h"
#include <jni.h>
/** Represents armeabi-v7a ABI with NEON support. */
#define ABI_ARMV7A_NEON "armeabi-v7a-neon"
@ -49,27 +49,40 @@
* Method: getNativeAbi
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi(JNIEnv *, jclass);
/*
* Class: com_arthenica_ffmpegkit_AbiDetect
* Method: getNativeCpuAbi
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeCpuAbi(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_getNativeCpuAbi(JNIEnv *, jclass);
/**
* Class: com_arthenica_ffmpegkit_AbiDetect
* Method: isNativeLTSBuild
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild(JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild(JNIEnv *, jclass);
/*
* Class: com_arthenica_ffmpegkit_AbiDetect
* Method: getNativeBuildConf
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeBuildConf(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_getNativeBuildConf(JNIEnv *, jclass);
/*
* Class: com_arthenica_ffmpegkit_AbiDetect
* Method: getNativeMinSdk
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_com_arthenica_ffmpegkit_AbiDetect_getNativeMinSdk(
JNIEnv *env, jclass object);
#endif /* FFMPEG_KIT_ABIDETECT_H */

@ -20,8 +20,8 @@
#ifndef FFMPEG_KIT_EXCEPTION_H
#define FFMPEG_KIT_EXCEPTION_H
#include <stdio.h>
#include <setjmp.h>
#include <stdio.h>
/** Holds information to implement exception handling. */
extern __thread jmp_buf ex_buf__;

@ -18,14 +18,14 @@
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "config.h"
#include "ffmpegkit.h"
#include "libavcodec/jni.h"
#include "libavutil/bprint.h"
#include "libavutil/mem.h"
#include "ffmpegkit.h"
/** Forward declaration for function defined in fftools_ffprobe.c */
int ffprobe_execute(int argc, char **argv);
@ -45,7 +45,9 @@ extern void resetMessagesInTransmit(long sessionId);
* @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_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute(JNIEnv *env, jclass object, jlong id, jobjectArray stringArray) {
JNIEXPORT jint JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute(
JNIEnv *env, jclass object, jlong id, jobjectArray stringArray) {
jstring *tempArray = NULL;
int argumentCount = 1;
char **argv = NULL;
@ -57,30 +59,33 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprob
int programArgumentCount = (*env)->GetArrayLength(env, stringArray);
argumentCount = programArgumentCount + 1;
tempArray = (jstring *) av_malloc(sizeof(jstring) * programArgumentCount);
tempArray =
(jstring *)av_malloc(sizeof(jstring) * programArgumentCount);
}
/* PRESERVE USAGE FORMAT
*
* ffprobe <arguments>
*/
argv = (char **)av_malloc(sizeof(char*) * (argumentCount));
argv = (char **)av_malloc(sizeof(char *) * (argumentCount));
argv[0] = (char *)av_malloc(sizeof(char) * (strlen(LIB_NAME) + 1));
strcpy(argv[0], LIB_NAME);
// PREPARE ARRAY ELEMENTS
if (stringArray) {
for (int i = 0; i < (argumentCount - 1); i++) {
tempArray[i] = (jstring) (*env)->GetObjectArrayElement(env, stringArray, i);
tempArray[i] =
(jstring)(*env)->GetObjectArrayElement(env, stringArray, i);
if (tempArray[i] != NULL) {
argv[i + 1] = (char *) (*env)->GetStringUTFChars(env, tempArray[i], 0);
argv[i + 1] =
(char *)(*env)->GetStringUTFChars(env, tempArray[i], 0);
}
}
}
// REGISTER THE ID BEFORE STARTING THE SESSION
globalSessionId = (long) id;
addSession((long) id);
globalSessionId = (long)id;
addSession((long)id);
resetMessagesInTransmit(globalSessionId);
@ -88,7 +93,7 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprob
int returnCode = ffprobe_execute(argumentCount, argv);
// ALWAYS REMOVE THE ID FROM THE MAP
removeSession((long) id);
removeSession((long)id);
// CLEANUP
if (tempArray) {

@ -27,6 +27,9 @@
* Method: nativeFFprobeExecute
* Signature: (J[Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute(JNIEnv *, jclass, jlong, jobjectArray);
JNIEXPORT jint JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute(JNIEnv *,
jclass, jlong,
jobjectArray);
#endif /* FFPROBE_KIT_H */

@ -2,7 +2,7 @@
* Various utilities for command line tools
* copyright (c) 2003 Fabrice Bellard
* copyright (c) 2018-2022 Taner Sener
* copyright (c) 2023 ARTHENICA LTD
* copyright (c) 2023-2024 ARTHENICA LTD
*
* This file is part of FFmpeg.
*
@ -22,12 +22,17 @@
*/
/*
* This file is the modified version of cmdutils.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* This file is the modified version of cmdutils.h file living in ffmpeg source
* code under the fftools folder. We manually update it each time we depend on a
* new ffmpeg version. Below you can see the list of changes applied by us to
* develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
@ -40,12 +45,14 @@
*
* 01.2020
* --------------------------------------------------------
* - ffprobe support added (variables used by ffprobe marked with "__thread" specifier)
* - ffprobe support added (variables used by ffprobe marked with "__thread"
* specifier)
* - AV_LOG_STDERR log level added
*
* 12.2019
* --------------------------------------------------------
* - concurrent execution support ("__thread" specifier added to variables used by multiple threads)
* - concurrent execution support ("__thread" specifier added to variables used
* by multiple threads)
*
* 03.2019
* --------------------------------------------------------
@ -77,9 +84,10 @@
#endif
/**
* Defines logs printed to stderr by ffmpeg. They are not filtered and always redirected.
* Defines logs printed to stderr by ffmpeg. They are not filtered and always
* redirected.
*/
#define AV_LOG_STDERR -16
#define AV_LOG_STDERR -16
/**
* program name, defined by the program for show_version().
@ -97,22 +105,6 @@ extern __thread AVDictionary *format_opts, *codec_opts;
extern __thread int hide_banner;
extern __thread int find_stream_info;
/**
* Register a program-specific cleanup routine.
*/
void register_exit(void (*cb)(int ret));
/**
* Reports an error corresponding to the provided
* AVERROR code and calls exit_program() with the
* corresponding POSIX error code.
* @note ret must be an AVERROR-value of a POSIX error code
* (i.e. AVERROR(EFOO) and not AVERROR_FOO).
* library functions can return both, so call this only
* with AVERROR(EFOO) of your own.
*/
void report_and_exit(int ret) av_noreturn;
/**
* Wraps exit with a program-specific cleanup routine.
*/
@ -129,12 +121,6 @@ void init_dynload(void);
*/
void uninit_opts(void);
/**
* Trivial log callback.
* Only suitable for opt_help and similar since it lacks prefix handling.
*/
void log_callback_help(void* ptr, int level, const char* fmt, va_list vl);
/**
* Fallback for options that are not explicitly handled, these will be
* parsed through AVOptions.
@ -148,8 +134,6 @@ int opt_timelimit(void *optctx, const char *opt, const char *arg);
/**
* Parse a string and return its corresponding value as a double.
* Exit from the application if the string cannot be correctly
* parsed or the corresponding value is invalid.
*
* @param context the context of the value to be set (e.g. the
* corresponding command line option name)
@ -159,64 +143,50 @@ int opt_timelimit(void *optctx, const char *opt, const char *arg);
* @param min the minimum valid accepted value
* @param max the maximum valid accepted value
*/
double parse_number_or_die(const char *context, const char *numstr, int type,
double min, double max);
/**
* Parse a string specifying a time and return its corresponding
* value as a number of microseconds. Exit from the application if
* the string cannot be correctly parsed.
*
* @param context the context of the value to be set (e.g. the
* corresponding command line option name)
* @param timestr the string to be parsed
* @param is_duration a flag which tells how to interpret timestr, if
* not zero timestr is interpreted as a duration, otherwise as a
* date
*
* @see av_parse_time()
*/
int64_t parse_time_or_die(const char *context, const char *timestr,
int is_duration);
int parse_number(const char *context, const char *numstr, int type, double min,
double max, double *dst);
typedef struct SpecifierOpt {
char *specifier; /**< stream/chapter/program/... specifier */
char *specifier; /**< stream/chapter/program/... specifier */
union {
uint8_t *str;
int i;
int64_t i64;
int i;
int64_t i64;
uint64_t ui64;
float f;
double dbl;
float f;
double dbl;
} u;
} SpecifierOpt;
typedef struct OptionDef {
const char *name;
int flags;
#define HAS_ARG 0x0001
#define OPT_BOOL 0x0002
#define HAS_ARG 0x0001
#define OPT_BOOL 0x0002
#define OPT_EXPERT 0x0004
#define OPT_STRING 0x0008
#define OPT_VIDEO 0x0010
#define OPT_AUDIO 0x0020
#define OPT_INT 0x0080
#define OPT_FLOAT 0x0100
#define OPT_VIDEO 0x0010
#define OPT_AUDIO 0x0020
#define OPT_INT 0x0080
#define OPT_FLOAT 0x0100
#define OPT_SUBTITLE 0x0200
#define OPT_INT64 0x0400
#define OPT_EXIT 0x0800
#define OPT_DATA 0x1000
#define OPT_PERFILE 0x2000 /* the option is per-file (currently ffmpeg-only).
implied by OPT_OFFSET or OPT_SPEC */
#define OPT_OFFSET 0x4000 /* option is specified as an offset in a passed optctx */
#define OPT_SPEC 0x8000 /* option is to be stored in an array of SpecifierOpt.
Implies OPT_OFFSET. Next element after the offset is
an int containing element count in the array. */
#define OPT_TIME 0x10000
#define OPT_INT64 0x0400
#define OPT_EXIT 0x0800
#define OPT_DATA 0x1000
#define OPT_PERFILE \
0x2000 /* the option is per-file (currently ffmpeg-only). \
implied by OPT_OFFSET or OPT_SPEC */
#define OPT_OFFSET \
0x4000 /* option is specified as an offset in a passed optctx */
#define OPT_SPEC \
0x8000 /* option is to be stored in an array of SpecifierOpt. \
Implies OPT_OFFSET. Next element after the offset is \
an int containing element count in the array. */
#define OPT_TIME 0x10000
#define OPT_DOUBLE 0x20000
#define OPT_INPUT 0x40000
#define OPT_INPUT 0x40000
#define OPT_OUTPUT 0x80000
union {
union {
void *dst_ptr;
int (*func_arg)(void *, const char *, const char *);
size_t off;
@ -262,13 +232,14 @@ void show_help_default_ffprobe(const char *opt, const char *arg);
* argument without a leading option name flag. NULL if such arguments do
* not have to be processed.
*/
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
void (* parse_arg_function)(void *optctx, const char*));
int parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
int (*parse_arg_function)(void *optctx, const char *));
/**
* Parse one given option.
*
* @return on success 1 if arg was consumed, 0 otherwise; negative number on error
* @return on success 1 if arg was consumed, 0 otherwise; negative number on
* error
*/
int parse_option(void *optctx, const char *opt, const char *arg,
const OptionDef *options);
@ -279,9 +250,9 @@ int parse_option(void *optctx, const char *opt, const char *arg,
* used multiple times.
*/
typedef struct Option {
const OptionDef *opt;
const char *key;
const char *val;
const OptionDef *opt;
const char *key;
const char *val;
} Option;
typedef struct OptionGroupDef {
@ -304,7 +275,7 @@ typedef struct OptionGroup {
const char *arg;
Option *opts;
int nb_opts;
int nb_opts;
AVDictionary *codec_opts;
AVDictionary *format_opts;
@ -320,14 +291,14 @@ typedef struct OptionGroupList {
const OptionGroupDef *group_def;
OptionGroup *groups;
int nb_groups;
int nb_groups;
} OptionGroupList;
typedef struct OptionParseContext {
OptionGroup global_opts;
OptionGroupList *groups;
int nb_groups;
int nb_groups;
/* parsing state */
OptionGroup cur_group;
@ -360,8 +331,8 @@ int parse_optgroup(void *optctx, OptionGroup *g);
* same as the order of group definitions.
*/
int split_commandline(OptionParseContext *octx, int argc, char *argv[],
const OptionDef *options,
const OptionGroupDef *groups, int nb_groups);
const OptionDef *options, const OptionGroupDef *groups,
int nb_groups);
/**
* Free all allocated memory in an OptionParseContext.
@ -402,10 +373,12 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec);
* @param st A stream from s for which the options should be filtered.
* @param codec The particular codec for which the options should be filtered.
* If null, the default one is looked up according to the codec id.
* @return a pointer to the created dictionary
* @param dst a pointer to the created dictionary
* @return a non-negative number on success, a negative error code on failure
*/
AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, const AVCodec *codec);
int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, const AVCodec *codec,
AVDictionary **dst);
/**
* Setup AVCodecContext options for avformat_find_stream_info().
@ -414,12 +387,9 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
* contained in s.
* Each dictionary will contain the options from codec_opts which can
* be applied to the corresponding stream codec context.
*
* @return pointer to the created array of dictionaries.
* Calls exit() on failure.
*/
AVDictionary **setup_find_stream_info_opts(AVFormatContext *s,
AVDictionary *codec_opts);
int setup_find_stream_info_opts(AVFormatContext *s, AVDictionary *codec_opts,
AVDictionary ***dst);
/**
* Print an error message to stderr, indicating filename and a human
@ -464,53 +434,49 @@ int read_yesno(void);
* preset, may be NULL
*/
FILE *get_preset_file(char *filename, size_t filename_size,
const char *preset_name, int is_path, const char *codec_name);
const char *preset_name, int is_path,
const char *codec_name);
/**
* Realloc array to hold new_size elements of elem_size.
* Calls exit() on failure.
*
* @param array array to reallocate
* @param array pointer to the array to reallocate, will be updated
* with a new pointer on success
* @param elem_size size in bytes of each element
* @param size new element count will be written here
* @param new_size number of elements to place in reallocated array
* @return reallocated array
* @return a non-negative number on success, a negative error code on failure
*/
void *grow_array(void *array, int elem_size, int *size, int new_size);
int grow_array(void **array, int elem_size, int *size, int new_size);
/**
* Atomically add a new element to an array of pointers, i.e. allocate
* a new entry, reallocate the array of pointers and make the new last
* member of this array point to the newly allocated buffer.
* Calls exit() on failure.
*
* @param array array of pointers to reallocate
* @param elem_size size of the new element to allocate
* @param nb_elems pointer to the number of elements of the array array;
* *nb_elems will be incremented by one by this function.
* @return pointer to the newly allocated entry
* @return pointer to the newly allocated entry or NULL on failure
*/
void *allocate_array_elem(void *array, size_t elem_size, int *nb_elems);
#define GROW_ARRAY(array, nb_elems)\
array = grow_array(array, sizeof(*array), &nb_elems, nb_elems + 1)
#define ALLOC_ARRAY_ELEM(array, nb_elems)\
allocate_array_elem(&array, sizeof(*array[0]), &nb_elems)
#define GROW_ARRAY(array, nb_elems) \
grow_array((void **)&array, sizeof(*array), &nb_elems, nb_elems + 1)
#define GET_PIX_FMT_NAME(pix_fmt)\
#define GET_PIX_FMT_NAME(pix_fmt) \
const char *name = av_get_pix_fmt_name(pix_fmt);
#define GET_CODEC_NAME(id)\
const char *name = avcodec_descriptor_get(id)->name;
#define GET_CODEC_NAME(id) const char *name = avcodec_descriptor_get(id)->name;
#define GET_SAMPLE_FMT_NAME(sample_fmt)\
#define GET_SAMPLE_FMT_NAME(sample_fmt) \
const char *name = av_get_sample_fmt_name(sample_fmt)
#define GET_SAMPLE_RATE_NAME(rate)\
char name[16];\
#define GET_SAMPLE_RATE_NAME(rate) \
char name[16]; \
snprintf(name, sizeof(name), "%d", rate);
double get_rotation(int32_t *displaymatrix);
double get_rotation(const int32_t *displaymatrix);
#endif /* FFTOOLS_CMDUTILS_H */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,962 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg 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 2.1 of the License, or (at your option) any later version.
*
* FFmpeg 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This file is the modified version of ffmpeg_enc.c file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - Migrated from FFmpeg 6.1
*/
#include <math.h>
#include <stdint.h>
#include "fftools_ffmpeg.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/avutil.h"
#include "libavutil/dict.h"
#include "libavutil/display.h"
#include "libavutil/eval.h"
#include "libavutil/frame.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/pixdesc.h"
#include "libavutil/rational.h"
#include "libavutil/timestamp.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
struct Encoder {
AVFrame *sq_frame;
// packet for receiving encoded output
AVPacket *pkt;
// combined size of all the packets received from the encoder
uint64_t data_size;
// number of packets received from the encoder
uint64_t packets_encoded;
int opened;
};
void enc_free(Encoder **penc) {
Encoder *enc = *penc;
if (!enc)
return;
av_frame_free(&enc->sq_frame);
av_packet_free(&enc->pkt);
av_freep(penc);
}
int enc_alloc(Encoder **penc, const AVCodec *codec) {
Encoder *enc;
*penc = NULL;
enc = av_mallocz(sizeof(*enc));
if (!enc)
return AVERROR(ENOMEM);
enc->pkt = av_packet_alloc();
if (!enc->pkt)
goto fail;
*penc = enc;
return 0;
fail:
enc_free(&enc);
return AVERROR(ENOMEM);
}
static int hw_device_setup_for_encode(OutputStream *ost,
AVBufferRef *frames_ref) {
const AVCodecHWConfig *config;
HWDevice *dev = NULL;
int i;
if (frames_ref && ((AVHWFramesContext *)frames_ref->data)->format ==
ost->enc_ctx->pix_fmt) {
// Matching format, will try to use hw_frames_ctx.
} else {
frames_ref = NULL;
}
for (i = 0;; i++) {
config = avcodec_get_hw_config(ost->enc_ctx->codec, i);
if (!config)
break;
if (frames_ref &&
config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX &&
(config->pix_fmt == AV_PIX_FMT_NONE ||
config->pix_fmt == ost->enc_ctx->pix_fmt)) {
av_log(ost->enc_ctx, AV_LOG_VERBOSE,
"Using input "
"frames context (format %s) with %s encoder.\n",
av_get_pix_fmt_name(ost->enc_ctx->pix_fmt),
ost->enc_ctx->codec->name);
ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref);
if (!ost->enc_ctx->hw_frames_ctx)
return AVERROR(ENOMEM);
return 0;
}
if (!dev && config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
dev = hw_device_get_by_type(config->device_type);
}
if (dev) {
av_log(ost->enc_ctx, AV_LOG_VERBOSE,
"Using device %s "
"(type %s) with %s encoder.\n",
dev->name, av_hwdevice_get_type_name(dev->type),
ost->enc_ctx->codec->name);
ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
if (!ost->enc_ctx->hw_device_ctx)
return AVERROR(ENOMEM);
} else {
// No device required, or no device available.
}
return 0;
}
static int set_encoder_id(OutputFile *of, OutputStream *ost) {
const char *cname = ost->enc_ctx->codec->name;
uint8_t *encoder_string;
int encoder_string_len;
if (av_dict_get(ost->st->metadata, "encoder", NULL, 0))
return 0;
encoder_string_len = sizeof(LIBAVCODEC_IDENT) + strlen(cname) + 2;
encoder_string = av_mallocz(encoder_string_len);
if (!encoder_string)
return AVERROR(ENOMEM);
if (!of->bitexact && !ost->bitexact)
av_strlcpy(encoder_string, LIBAVCODEC_IDENT " ", encoder_string_len);
else
av_strlcpy(encoder_string, "Lavc ", encoder_string_len);
av_strlcat(encoder_string, cname, encoder_string_len);
av_dict_set(&ost->st->metadata, "encoder", encoder_string,
AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE);
return 0;
}
int enc_open(OutputStream *ost, const AVFrame *frame) {
InputStream *ist = ost->ist;
Encoder *e = ost->enc;
AVCodecContext *enc_ctx = ost->enc_ctx;
AVCodecContext *dec_ctx = NULL;
const AVCodec *enc = enc_ctx->codec;
OutputFile *of = output_files[ost->file_index];
FrameData *fd;
int ret;
if (e->opened)
return 0;
// frame is always non-NULL for audio and video
av_assert0(frame || (enc->type != AVMEDIA_TYPE_VIDEO &&
enc->type != AVMEDIA_TYPE_AUDIO));
if (frame) {
av_assert0(frame->opaque_ref);
fd = (FrameData *)frame->opaque_ref->data;
}
ret = set_encoder_id(output_files[ost->file_index], ost);
if (ret < 0)
return ret;
if (ist) {
dec_ctx = ist->dec_ctx;
}
// the timebase is chosen by filtering code
if (ost->type == AVMEDIA_TYPE_AUDIO || ost->type == AVMEDIA_TYPE_VIDEO) {
enc_ctx->time_base = frame->time_base;
enc_ctx->framerate = fd->frame_rate_filter;
ost->st->avg_frame_rate = fd->frame_rate_filter;
}
switch (enc_ctx->codec_type) {
case AVMEDIA_TYPE_AUDIO:
enc_ctx->sample_fmt = frame->format;
enc_ctx->sample_rate = frame->sample_rate;
ret = av_channel_layout_copy(&enc_ctx->ch_layout, &frame->ch_layout);
if (ret < 0)
return ret;
if (ost->bits_per_raw_sample)
enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample;
else
enc_ctx->bits_per_raw_sample =
FFMIN(fd->bits_per_raw_sample,
av_get_bytes_per_sample(enc_ctx->sample_fmt) << 3);
break;
case AVMEDIA_TYPE_VIDEO: {
enc_ctx->width = frame->width;
enc_ctx->height = frame->height;
enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio =
ost->frame_aspect_ratio.num
? // overridden by the -aspect cli option
av_mul_q(ost->frame_aspect_ratio,
(AVRational){enc_ctx->height, enc_ctx->width})
: frame->sample_aspect_ratio;
enc_ctx->pix_fmt = frame->format;
if (ost->bits_per_raw_sample)
enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample;
else
enc_ctx->bits_per_raw_sample =
FFMIN(fd->bits_per_raw_sample,
av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth);
enc_ctx->color_range = frame->color_range;
enc_ctx->color_primaries = frame->color_primaries;
enc_ctx->color_trc = frame->color_trc;
enc_ctx->colorspace = frame->colorspace;
enc_ctx->chroma_sample_location = frame->chroma_location;
if (enc_ctx->flags &
(AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME) ||
(frame->flags & AV_FRAME_FLAG_INTERLACED)
#if FFMPEG_OPT_TOP
|| ost->top_field_first >= 0
#endif
) {
int top_field_first =
#if FFMPEG_OPT_TOP
ost->top_field_first >= 0
? ost->top_field_first
:
#endif
!!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST);
if (enc->id == AV_CODEC_ID_MJPEG)
enc_ctx->field_order =
top_field_first ? AV_FIELD_TT : AV_FIELD_BB;
else
enc_ctx->field_order =
top_field_first ? AV_FIELD_TB : AV_FIELD_BT;
} else
enc_ctx->field_order = AV_FIELD_PROGRESSIVE;
break;
}
case AVMEDIA_TYPE_SUBTITLE:
if (ost->enc_timebase.num)
av_log(ost, AV_LOG_WARNING,
"-enc_time_base not supported for subtitles, ignoring\n");
enc_ctx->time_base = AV_TIME_BASE_Q;
if (!enc_ctx->width) {
enc_ctx->width = ost->ist->par->width;
enc_ctx->height = ost->ist->par->height;
}
if (dec_ctx && dec_ctx->subtitle_header) {
/* ASS code assumes this buffer is null terminated so add extra
* byte. */
enc_ctx->subtitle_header =
av_mallocz(dec_ctx->subtitle_header_size + 1);
if (!enc_ctx->subtitle_header)
return AVERROR(ENOMEM);
memcpy(enc_ctx->subtitle_header, dec_ctx->subtitle_header,
dec_ctx->subtitle_header_size);
enc_ctx->subtitle_header_size = dec_ctx->subtitle_header_size;
}
break;
default:
av_assert0(0);
break;
}
if (ost->bitexact)
enc_ctx->flags |= AV_CODEC_FLAG_BITEXACT;
if (!av_dict_get(ost->encoder_opts, "threads", NULL, 0))
av_dict_set(&ost->encoder_opts, "threads", "auto", 0);
if (enc->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE) {
ret = av_dict_set(&ost->encoder_opts, "flags", "+copy_opaque",
AV_DICT_MULTIKEY);
if (ret < 0)
return ret;
}
av_dict_set(&ost->encoder_opts, "flags", "+frame_duration",
AV_DICT_MULTIKEY);
ret = hw_device_setup_for_encode(ost, frame ? frame->hw_frames_ctx : NULL);
if (ret < 0) {
av_log(ost, AV_LOG_ERROR, "Encoding hardware device setup failed: %s\n",
av_err2str(ret));
return ret;
}
if ((ret = avcodec_open2(ost->enc_ctx, enc, &ost->encoder_opts)) < 0) {
if (ret != AVERROR_EXPERIMENTAL)
av_log(ost, AV_LOG_ERROR,
"Error while opening encoder - maybe "
"incorrect parameters such as bit_rate, rate, width or "
"height.\n");
return ret;
}
e->opened = 1;
if (ost->sq_idx_encode >= 0) {
e->sq_frame = av_frame_alloc();
if (!e->sq_frame)
return AVERROR(ENOMEM);
}
if (ost->enc_ctx->frame_size) {
av_assert0(ost->sq_idx_encode >= 0);
sq_frame_samples(output_files[ost->file_index]->sq_encode,
ost->sq_idx_encode, ost->enc_ctx->frame_size);
}
ret = check_avoptions(ost->encoder_opts);
if (ret < 0)
return ret;
if (ost->enc_ctx->bit_rate && ost->enc_ctx->bit_rate < 1000 &&
ost->enc_ctx->codec_id !=
AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */)
av_log(ost, AV_LOG_WARNING,
"The bitrate parameter is set too low."
" It takes bits/s as argument, not kbits/s\n");
ret = avcodec_parameters_from_context(ost->par_in, ost->enc_ctx);
if (ret < 0) {
av_log(ost, AV_LOG_FATAL,
"Error initializing the output stream codec context.\n");
return ret;
}
/*
* Add global input side data. For now this is naive, and copies it
* from the input stream's global side data. All side data should
* really be funneled over AVFrame and libavfilter, then added back to
* packet side data, and then potentially using the first packet for
* global side data.
*/
if (ist) {
int i;
for (i = 0; i < ist->st->codecpar->nb_coded_side_data; i++) {
AVPacketSideData *sd_src = &ist->st->codecpar->coded_side_data[i];
if (sd_src->type != AV_PKT_DATA_CPB_PROPERTIES) {
AVPacketSideData *sd_dst =
av_packet_side_data_new(&ost->par_in->coded_side_data,
&ost->par_in->nb_coded_side_data,
sd_src->type, sd_src->size, 0);
if (!sd_dst)
return AVERROR(ENOMEM);
memcpy(sd_dst->data, sd_src->data, sd_src->size);
if (ist->autorotate &&
sd_src->type == AV_PKT_DATA_DISPLAYMATRIX)
av_display_rotation_set((int32_t *)sd_dst->data, 0);
}
}
}
// copy timebase while removing common factors
if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0)
ost->st->time_base =
av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1});
ret = of_stream_init(of, ost);
if (ret < 0)
return ret;
return 0;
}
static int check_recording_time(OutputStream *ost, int64_t ts, AVRational tb) {
OutputFile *of = output_files[ost->file_index];
if (of->recording_time != INT64_MAX &&
av_compare_ts(ts, tb, of->recording_time, AV_TIME_BASE_Q) >= 0) {
close_output_stream(ost);
return 0;
}
return 1;
}
int enc_subtitle(OutputFile *of, OutputStream *ost, const AVSubtitle *sub) {
Encoder *e = ost->enc;
int subtitle_out_max_size = 1024 * 1024;
int subtitle_out_size, nb, i, ret;
AVCodecContext *enc;
AVPacket *pkt = e->pkt;
int64_t pts;
if (sub->pts == AV_NOPTS_VALUE) {
av_log(ost, AV_LOG_ERROR, "Subtitle packets must have a pts\n");
return exit_on_error ? AVERROR(EINVAL) : 0;
}
if (ost->finished ||
(of->start_time != AV_NOPTS_VALUE && sub->pts < of->start_time))
return 0;
enc = ost->enc_ctx;
/* Note: DVB subtitle need one packet to draw them and one other
packet to clear them */
/* XXX: signal it in the codec context ? */
if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE)
nb = 2;
else if (enc->codec_id == AV_CODEC_ID_ASS)
nb = FFMAX(sub->num_rects, 1);
else
nb = 1;
/* shift timestamp to honor -ss and make check_recording_time() work with -t
*/
pts = sub->pts;
if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE)
pts -= output_files[ost->file_index]->start_time;
for (i = 0; i < nb; i++) {
AVSubtitle local_sub = *sub;
if (!check_recording_time(ost, pts, AV_TIME_BASE_Q))
return 0;
ret = av_new_packet(pkt, subtitle_out_max_size);
if (ret < 0)
return AVERROR(ENOMEM);
local_sub.pts = pts;
// start_display_time is required to be 0
local_sub.pts += av_rescale_q(sub->start_display_time,
(AVRational){1, 1000}, AV_TIME_BASE_Q);
local_sub.end_display_time -= sub->start_display_time;
local_sub.start_display_time = 0;
if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE && i == 1)
local_sub.num_rects = 0;
else if (enc->codec_id == AV_CODEC_ID_ASS && sub->num_rects > 0) {
local_sub.num_rects = 1;
local_sub.rects += i;
}
ost->frames_encoded++;
subtitle_out_size =
avcodec_encode_subtitle(enc, pkt->data, pkt->size, &local_sub);
if (subtitle_out_size < 0) {
av_log(ost, AV_LOG_FATAL, "Subtitle encoding failed\n");
return subtitle_out_size;
}
av_shrink_packet(pkt, subtitle_out_size);
pkt->time_base = AV_TIME_BASE_Q;
pkt->pts = sub->pts;
pkt->duration = av_rescale_q(sub->end_display_time,
(AVRational){1, 1000}, pkt->time_base);
if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
/* XXX: the pts correction is handled here. Maybe handling
it in the codec would be better */
if (i == 0)
pkt->pts += av_rescale_q(sub->start_display_time,
(AVRational){1, 1000}, pkt->time_base);
else
pkt->pts += av_rescale_q(sub->end_display_time,
(AVRational){1, 1000}, pkt->time_base);
}
pkt->dts = pkt->pts;
ret = of_output_packet(of, ost, pkt);
if (ret < 0)
return ret;
}
return 0;
}
void enc_stats_write(OutputStream *ost, EncStats *es, const AVFrame *frame,
const AVPacket *pkt, uint64_t frame_num) {
Encoder *e = ost->enc;
AVIOContext *io = es->io;
AVRational tb = frame ? frame->time_base : pkt->time_base;
int64_t pts = frame ? frame->pts : pkt->pts;
AVRational tbi = (AVRational){0, 1};
int64_t ptsi = INT64_MAX;
const FrameData *fd = NULL;
if (frame ? frame->opaque_ref : pkt->opaque_ref) {
fd = (const FrameData *)(frame ? frame->opaque_ref->data
: pkt->opaque_ref->data);
tbi = fd->dec.tb;
ptsi = fd->dec.pts;
}
for (size_t i = 0; i < es->nb_components; i++) {
const EncStatsComponent *c = &es->components[i];
switch (c->type) {
case ENC_STATS_LITERAL:
avio_write(io, c->str, c->str_len);
continue;
case ENC_STATS_FILE_IDX:
avio_printf(io, "%d", ost->file_index);
continue;
case ENC_STATS_STREAM_IDX:
avio_printf(io, "%d", ost->index);
continue;
case ENC_STATS_TIMEBASE:
avio_printf(io, "%d/%d", tb.num, tb.den);
continue;
case ENC_STATS_TIMEBASE_IN:
avio_printf(io, "%d/%d", tbi.num, tbi.den);
continue;
case ENC_STATS_PTS:
avio_printf(io, "%" PRId64, pts);
continue;
case ENC_STATS_PTS_IN:
avio_printf(io, "%" PRId64, ptsi);
continue;
case ENC_STATS_PTS_TIME:
avio_printf(io, "%g", pts * av_q2d(tb));
continue;
case ENC_STATS_PTS_TIME_IN:
avio_printf(io, "%g",
ptsi == INT64_MAX ? INFINITY : ptsi * av_q2d(tbi));
continue;
case ENC_STATS_FRAME_NUM:
avio_printf(io, "%" PRIu64, frame_num);
continue;
case ENC_STATS_FRAME_NUM_IN:
avio_printf(io, "%" PRIu64, fd ? fd->dec.frame_num : -1);
continue;
}
if (frame) {
switch (c->type) {
case ENC_STATS_SAMPLE_NUM:
avio_printf(io, "%" PRIu64, ost->samples_encoded);
continue;
case ENC_STATS_NB_SAMPLES:
avio_printf(io, "%d", frame->nb_samples);
continue;
default:
av_assert0(0);
}
} else {
switch (c->type) {
case ENC_STATS_DTS:
avio_printf(io, "%" PRId64, pkt->dts);
continue;
case ENC_STATS_DTS_TIME:
avio_printf(io, "%g", pkt->dts * av_q2d(tb));
continue;
case ENC_STATS_PKT_SIZE:
avio_printf(io, "%d", pkt->size);
continue;
case ENC_STATS_BITRATE: {
double duration = FFMAX(pkt->duration, 1) * av_q2d(tb);
avio_printf(io, "%g", 8.0 * pkt->size / duration);
continue;
}
case ENC_STATS_AVG_BITRATE: {
double duration = pkt->dts * av_q2d(tb);
avio_printf(io, "%g",
duration > 0 ? 8.0 * e->data_size / duration : -1.);
continue;
}
default:
av_assert0(0);
}
}
}
avio_w8(io, '\n');
avio_flush(io);
}
static inline double psnr(double d) { return -10.0 * log10(d); }
static int update_video_stats(OutputStream *ost, const AVPacket *pkt,
int write_vstats) {
Encoder *e = ost->enc;
const uint8_t *sd =
av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS, NULL);
AVCodecContext *enc = ost->enc_ctx;
enum AVPictureType pict_type;
int64_t frame_number;
double ti1, bitrate, avg_bitrate;
double psnr_val = -1;
ost->quality = sd ? AV_RL32(sd) : -1;
pict_type = sd ? sd[4] : AV_PICTURE_TYPE_NONE;
if ((enc->flags & AV_CODEC_FLAG_PSNR) && sd && sd[5]) {
// FIXME the scaling assumes 8bit
double error =
AV_RL64(sd + 8) / (enc->width * enc->height * 255.0 * 255.0);
if (error >= 0 && error <= 1)
psnr_val = psnr(error);
}
if (!write_vstats)
return 0;
/* this is executed just the first time update_video_stats is called */
if (!vstats_file) {
vstats_file = fopen(vstats_filename, "w");
if (!vstats_file) {
perror("fopen");
return AVERROR(errno);
}
}
frame_number = e->packets_encoded;
if (vstats_version <= 1) {
fprintf(vstats_file, "frame= %5" PRId64 " q= %2.1f ", frame_number,
ost->quality / (float)FF_QP2LAMBDA);
} else {
fprintf(vstats_file, "out= %2d st= %2d frame= %5" PRId64 " q= %2.1f ",
ost->file_index, ost->index, frame_number,
ost->quality / (float)FF_QP2LAMBDA);
}
if (psnr_val >= 0)
fprintf(vstats_file, "PSNR= %6.2f ", psnr_val);
fprintf(vstats_file, "f_size= %6d ", pkt->size);
/* compute pts value */
ti1 = pkt->dts * av_q2d(pkt->time_base);
if (ti1 < 0.01)
ti1 = 0.01;
bitrate = (pkt->size * 8) / av_q2d(enc->time_base) / 1000.0;
avg_bitrate = (double)(e->data_size * 8) / ti1 / 1000.0;
fprintf(
vstats_file,
"s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ",
(double)e->data_size / 1024, ti1, bitrate, avg_bitrate);
fprintf(vstats_file, "type= %c\n", av_get_picture_type_char(pict_type));
return 0;
}
static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) {
Encoder *e = ost->enc;
AVCodecContext *enc = ost->enc_ctx;
AVPacket *pkt = e->pkt;
const char *type_desc = av_get_media_type_string(enc->codec_type);
const char *action = frame ? "encode" : "flush";
int ret;
if (frame) {
if (ost->enc_stats_pre.io)
enc_stats_write(ost, &ost->enc_stats_pre, frame, NULL,
ost->frames_encoded);
ost->frames_encoded++;
ost->samples_encoded += frame->nb_samples;
if (debug_ts) {
av_log(ost, AV_LOG_INFO,
"encoder <- type:%s "
"frame_pts:%s frame_pts_time:%s time_base:%d/%d\n",
type_desc, av_ts2str(frame->pts),
av_ts2timestr(frame->pts, &enc->time_base),
enc->time_base.num, enc->time_base.den);
}
if (frame->sample_aspect_ratio.num && !ost->frame_aspect_ratio.num)
enc->sample_aspect_ratio = frame->sample_aspect_ratio;
}
update_benchmark(NULL);
ret = avcodec_send_frame(enc, frame);
if (ret < 0 && !(ret == AVERROR_EOF && !frame)) {
av_log(ost, AV_LOG_ERROR, "Error submitting %s frame to the encoder\n",
type_desc);
return ret;
}
while (1) {
av_packet_unref(pkt);
ret = avcodec_receive_packet(enc, pkt);
update_benchmark("%s_%s %d.%d", action, type_desc, ost->file_index,
ost->index);
pkt->time_base = enc->time_base;
/* if two pass, output log on success and EOF */
if ((ret >= 0 || ret == AVERROR_EOF) && ost->logfile && enc->stats_out)
fprintf(ost->logfile, "%s", enc->stats_out);
if (ret == AVERROR(EAGAIN)) {
av_assert0(frame); // should never happen during flushing
return 0;
} else if (ret == AVERROR_EOF) {
ret = of_output_packet(of, ost, NULL);
return ret < 0 ? ret : AVERROR_EOF;
} else if (ret < 0) {
av_log(ost, AV_LOG_ERROR, "%s encoding failed\n", type_desc);
return ret;
}
if (enc->codec_type == AVMEDIA_TYPE_VIDEO) {
ret = update_video_stats(ost, pkt, !!vstats_filename);
if (ret < 0)
return ret;
}
if (ost->enc_stats_post.io)
enc_stats_write(ost, &ost->enc_stats_post, NULL, pkt,
e->packets_encoded);
if (debug_ts) {
av_log(ost, AV_LOG_INFO,
"encoder -> type:%s "
"pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s "
"duration:%s duration_time:%s\n",
type_desc, av_ts2str(pkt->pts),
av_ts2timestr(pkt->pts, &enc->time_base),
av_ts2str(pkt->dts),
av_ts2timestr(pkt->dts, &enc->time_base),
av_ts2str(pkt->duration),
av_ts2timestr(pkt->duration, &enc->time_base));
}
if ((ret = trigger_fix_sub_duration_heartbeat(ost, pkt)) < 0) {
av_log(NULL, AV_LOG_ERROR,
"Subtitle heartbeat logic failed in %s! (%s)\n", __func__,
av_err2str(ret));
return ret;
}
e->data_size += pkt->size;
e->packets_encoded++;
ret = of_output_packet(of, ost, pkt);
if (ret < 0)
return ret;
}
av_assert0(0);
}
static int submit_encode_frame(OutputFile *of, OutputStream *ost,
AVFrame *frame) {
Encoder *e = ost->enc;
int ret;
if (ost->sq_idx_encode < 0)
return encode_frame(of, ost, frame);
if (frame) {
ret = av_frame_ref(e->sq_frame, frame);
if (ret < 0)
return ret;
frame = e->sq_frame;
}
ret = sq_send(of->sq_encode, ost->sq_idx_encode, SQFRAME(frame));
if (ret < 0) {
if (frame)
av_frame_unref(frame);
if (ret != AVERROR_EOF)
return ret;
}
while (1) {
AVFrame *enc_frame = e->sq_frame;
ret = sq_receive(of->sq_encode, ost->sq_idx_encode, SQFRAME(enc_frame));
if (ret == AVERROR_EOF) {
enc_frame = NULL;
} else if (ret < 0) {
return (ret == AVERROR(EAGAIN)) ? 0 : ret;
}
ret = encode_frame(of, ost, enc_frame);
if (enc_frame)
av_frame_unref(enc_frame);
if (ret < 0) {
if (ret == AVERROR_EOF)
close_output_stream(ost);
return ret;
}
}
}
static int do_audio_out(OutputFile *of, OutputStream *ost, AVFrame *frame) {
AVCodecContext *enc = ost->enc_ctx;
int ret;
if (!(enc->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) &&
enc->ch_layout.nb_channels != frame->ch_layout.nb_channels) {
av_log(ost, AV_LOG_ERROR,
"Audio channel count changed and encoder does not support "
"parameter "
"changes\n");
return 0;
}
if (!check_recording_time(ost, frame->pts, frame->time_base))
return 0;
ret = submit_encode_frame(of, ost, frame);
return (ret < 0 && ret != AVERROR_EOF) ? ret : 0;
}
static enum AVPictureType forced_kf_apply(void *logctx, KeyframeForceCtx *kf,
AVRational tb,
const AVFrame *in_picture) {
double pts_time;
if (kf->ref_pts == AV_NOPTS_VALUE)
kf->ref_pts = in_picture->pts;
pts_time = (in_picture->pts - kf->ref_pts) * av_q2d(tb);
if (kf->index < kf->nb_pts &&
av_compare_ts(in_picture->pts, tb, kf->pts[kf->index],
AV_TIME_BASE_Q) >= 0) {
kf->index++;
goto force_keyframe;
} else if (kf->pexpr) {
double res;
kf->expr_const_values[FKF_T] = pts_time;
res = av_expr_eval(kf->pexpr, kf->expr_const_values, NULL);
av_log(logctx, AV_LOG_TRACE,
"force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f "
"prev_forced_t:%f -> res:%f\n",
kf->expr_const_values[FKF_N],
kf->expr_const_values[FKF_N_FORCED],
kf->expr_const_values[FKF_PREV_FORCED_N],
kf->expr_const_values[FKF_T],
kf->expr_const_values[FKF_PREV_FORCED_T], res);
kf->expr_const_values[FKF_N] += 1;
if (res) {
kf->expr_const_values[FKF_PREV_FORCED_N] =
kf->expr_const_values[FKF_N] - 1;
kf->expr_const_values[FKF_PREV_FORCED_T] =
kf->expr_const_values[FKF_T];
kf->expr_const_values[FKF_N_FORCED] += 1;
goto force_keyframe;
}
} else if (kf->type == KF_FORCE_SOURCE &&
(in_picture->flags & AV_FRAME_FLAG_KEY)) {
goto force_keyframe;
}
return AV_PICTURE_TYPE_NONE;
force_keyframe:
av_log(logctx, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time);
return AV_PICTURE_TYPE_I;
}
/* May modify/reset frame */
static int do_video_out(OutputFile *of, OutputStream *ost,
AVFrame *in_picture) {
int ret;
AVCodecContext *enc = ost->enc_ctx;
if (!check_recording_time(ost, in_picture->pts, ost->enc_ctx->time_base))
return 0;
in_picture->quality = enc->global_quality;
in_picture->pict_type =
forced_kf_apply(ost, &ost->kf, enc->time_base, in_picture);
#if FFMPEG_OPT_TOP
if (ost->top_field_first >= 0) {
in_picture->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST;
in_picture->flags |=
AV_FRAME_FLAG_TOP_FIELD_FIRST * (!!ost->top_field_first);
}
#endif
ret = submit_encode_frame(of, ost, in_picture);
return (ret == AVERROR_EOF) ? 0 : ret;
}
int enc_frame(OutputStream *ost, AVFrame *frame) {
OutputFile *of = output_files[ost->file_index];
int ret;
ret = enc_open(ost, frame);
if (ret < 0)
return ret;
return ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO
? do_video_out(of, ost, frame)
: do_audio_out(of, ost, frame);
}
int enc_flush(void) {
int ret;
for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) {
OutputFile *of = output_files[ost->file_index];
if (ost->sq_idx_encode >= 0)
sq_send(of->sq_encode, ost->sq_idx_encode, SQFRAME(NULL));
}
for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) {
Encoder *e = ost->enc;
AVCodecContext *enc = ost->enc_ctx;
OutputFile *of = output_files[ost->file_index];
if (!enc || !e->opened ||
(enc->codec_type != AVMEDIA_TYPE_VIDEO &&
enc->codec_type != AVMEDIA_TYPE_AUDIO))
continue;
ret = submit_encode_frame(of, ost, NULL);
if (ret != AVERROR_EOF)
return ret;
}
return 0;
}

@ -1,6 +1,6 @@
/*
* Copyright (c) 2018-2019 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
* Copyright (c) 2023-2024 ARTHENICA LTD
*
* This file is part of FFmpeg.
*
@ -20,12 +20,17 @@
*/
/*
* This file is the modified version of ffmpeg_hw.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* This file is the modified version of ffmpeg_hw.c file living in ffmpeg source
* code under the fftools folder. We manually update it each time we depend on a
* new ffmpeg version. Below you can see the list of changes applied by us to
* develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
@ -34,7 +39,8 @@
*
* 12.2019
* --------------------------------------------------------
* - concurrent execution support ("__thread" specifier added to variables used by multiple threads)
* - concurrent execution support ("__thread" specifier added to variables used
* by multiple threads)
*
* 08.2018
* --------------------------------------------------------
@ -43,17 +49,16 @@
#include <string.h>
#include "libavfilter/buffersink.h"
#include "libavutil/avstring.h"
#include "libavutil/pixdesc.h"
#include "libavfilter/buffersink.h"
#include "fftools_ffmpeg.h"
__thread int nb_hw_devices;
__thread HWDevice **hw_devices;
static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type)
{
HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) {
HWDevice *found = NULL;
int i;
for (i = 0; i < nb_hw_devices; i++) {
@ -66,8 +71,7 @@ static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type)
return found;
}
HWDevice *hw_device_get_by_name(const char *name)
{
HWDevice *hw_device_get_by_name(const char *name) {
int i;
for (i = 0; i < nb_hw_devices; i++) {
if (!strcmp(hw_devices[i]->name, name))
@ -76,11 +80,10 @@ HWDevice *hw_device_get_by_name(const char *name)
return NULL;
}
static HWDevice *hw_device_add(void)
{
static HWDevice *hw_device_add(void) {
int err;
err = av_reallocp_array(&hw_devices, nb_hw_devices + 1,
sizeof(*hw_devices));
err =
av_reallocp_array(&hw_devices, nb_hw_devices + 1, sizeof(*hw_devices));
if (err) {
nb_hw_devices = 0;
return NULL;
@ -91,8 +94,7 @@ static HWDevice *hw_device_add(void)
return hw_devices[nb_hw_devices++];
}
static char *hw_device_default_name(enum AVHWDeviceType type)
{
static char *hw_device_default_name(enum AVHWDeviceType type) {
// Make an automatic name of the form "type%d". We arbitrarily
// limit at 1000 anonymous devices of the same type - there is
// probably something else very wrong if you get to this limit.
@ -116,8 +118,7 @@ static char *hw_device_default_name(enum AVHWDeviceType type)
return name;
}
int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
{
int hw_device_init_from_string(const char *arg, HWDevice **dev_out) {
// "type=name"
// "type=name,key=value,key2=value2"
// "type=name:device,key=value,key2=value2"
@ -174,8 +175,7 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
if (!*p) {
// New device with no parameters.
err = av_hwdevice_ctx_create(&device_ref, type,
NULL, NULL, 0);
err = av_hwdevice_ctx_create(&device_ref, type, NULL, NULL, 0);
if (err < 0)
goto fail;
@ -199,7 +199,9 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
}
err = av_hwdevice_ctx_create(&device_ref, type,
q ? device : p[0] ? p : NULL,
q ? device
: p[0] ? p
: NULL,
options, 0);
if (err < 0)
goto fail;
@ -213,8 +215,8 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
goto invalid;
}
err = av_hwdevice_ctx_create_derived(&device_ref, type,
src->device_ref, 0);
err = av_hwdevice_ctx_create_derived(&device_ref, type, src->device_ref,
0);
if (err < 0)
goto fail;
} else if (*p == ',') {
@ -225,8 +227,7 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
goto invalid;
}
err = av_hwdevice_ctx_create(&device_ref, type,
NULL, options, 0);
err = av_hwdevice_ctx_create(&device_ref, type, NULL, options, 0);
if (err < 0)
goto fail;
} else {
@ -256,21 +257,18 @@ done:
av_dict_free(&options);
return err;
invalid:
av_log(NULL, AV_LOG_ERROR,
"Invalid device specification \"%s\": %s\n", arg, errmsg);
av_log(NULL, AV_LOG_ERROR, "Invalid device specification \"%s\": %s\n", arg,
errmsg);
err = AVERROR(EINVAL);
goto done;
fail:
av_log(NULL, AV_LOG_ERROR,
"Device creation failed: %d.\n", err);
av_log(NULL, AV_LOG_ERROR, "Device creation failed: %d.\n", err);
av_buffer_unref(&device_ref);
goto done;
}
static int hw_device_init_from_type(enum AVHWDeviceType type,
const char *device,
HWDevice **dev_out)
{
int hw_device_init_from_type(enum AVHWDeviceType type, const char *device,
HWDevice **dev_out) {
AVBufferRef *device_ref = NULL;
HWDevice *dev;
char *name;
@ -284,8 +282,7 @@ static int hw_device_init_from_type(enum AVHWDeviceType type,
err = av_hwdevice_ctx_create(&device_ref, type, device, NULL, 0);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR,
"Device creation failed: %d.\n", err);
av_log(NULL, AV_LOG_ERROR, "Device creation failed: %d.\n", err);
goto fail;
}
@ -310,8 +307,7 @@ fail:
return err;
}
void hw_device_free_all(void)
{
void hw_device_free_all(void) {
int i;
for (i = 0; i < nb_hw_devices; i++) {
av_freep(&hw_devices[i]->name);
@ -322,208 +318,7 @@ void hw_device_free_all(void)
nb_hw_devices = 0;
}
static HWDevice *hw_device_match_by_codec(const AVCodec *codec)
{
const AVCodecHWConfig *config;
HWDevice *dev;
int i;
for (i = 0;; i++) {
config = avcodec_get_hw_config(codec, i);
if (!config)
return NULL;
if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
continue;
dev = hw_device_get_by_type(config->device_type);
if (dev)
return dev;
}
}
int hw_device_setup_for_decode(InputStream *ist)
{
const AVCodecHWConfig *config;
enum AVHWDeviceType type;
HWDevice *dev = NULL;
int err, auto_device = 0;
if (ist->hwaccel_device) {
dev = hw_device_get_by_name(ist->hwaccel_device);
if (!dev) {
if (ist->hwaccel_id == HWACCEL_AUTO) {
auto_device = 1;
} else if (ist->hwaccel_id == HWACCEL_GENERIC) {
type = ist->hwaccel_device_type;
err = hw_device_init_from_type(type, ist->hwaccel_device,
&dev);
} else {
// This will be dealt with by API-specific initialisation
// (using hwaccel_device), so nothing further needed here.
return 0;
}
} else {
if (ist->hwaccel_id == HWACCEL_AUTO) {
ist->hwaccel_device_type = dev->type;
} else if (ist->hwaccel_device_type != dev->type) {
av_log(NULL, AV_LOG_ERROR, "Invalid hwaccel device "
"specified for decoder: device %s of type %s is not "
"usable with hwaccel %s.\n", dev->name,
av_hwdevice_get_type_name(dev->type),
av_hwdevice_get_type_name(ist->hwaccel_device_type));
return AVERROR(EINVAL);
}
}
} else {
if (ist->hwaccel_id == HWACCEL_AUTO) {
auto_device = 1;
} else if (ist->hwaccel_id == HWACCEL_GENERIC) {
type = ist->hwaccel_device_type;
dev = hw_device_get_by_type(type);
// When "-qsv_device device" is used, an internal QSV device named
// as "__qsv_device" is created. Another QSV device is created too
// if "-init_hw_device qsv=name:device" is used. There are 2 QSV devices
// if both "-qsv_device device" and "-init_hw_device qsv=name:device"
// are used, hw_device_get_by_type(AV_HWDEVICE_TYPE_QSV) returns NULL.
// To keep back-compatibility with the removed ad-hoc libmfx setup code,
// call hw_device_get_by_name("__qsv_device") to select the internal QSV
// device.
if (!dev && type == AV_HWDEVICE_TYPE_QSV)
dev = hw_device_get_by_name("__qsv_device");
if (!dev)
err = hw_device_init_from_type(type, NULL, &dev);
} else {
dev = hw_device_match_by_codec(ist->dec);
if (!dev) {
// No device for this codec, but not using generic hwaccel
// and therefore may well not need one - ignore.
return 0;
}
}
}
if (auto_device) {
int i;
if (!avcodec_get_hw_config(ist->dec, 0)) {
// Decoder does not support any hardware devices.
return 0;
}
for (i = 0; !dev; i++) {
config = avcodec_get_hw_config(ist->dec, i);
if (!config)
break;
type = config->device_type;
dev = hw_device_get_by_type(type);
if (dev) {
av_log(NULL, AV_LOG_INFO, "Using auto "
"hwaccel type %s with existing device %s.\n",
av_hwdevice_get_type_name(type), dev->name);
}
}
for (i = 0; !dev; i++) {
config = avcodec_get_hw_config(ist->dec, i);
if (!config)
break;
type = config->device_type;
// Try to make a new device of this type.
err = hw_device_init_from_type(type, ist->hwaccel_device,
&dev);
if (err < 0) {
// Can't make a device of this type.
continue;
}
if (ist->hwaccel_device) {
av_log(NULL, AV_LOG_INFO, "Using auto "
"hwaccel type %s with new device created "
"from %s.\n", av_hwdevice_get_type_name(type),
ist->hwaccel_device);
} else {
av_log(NULL, AV_LOG_INFO, "Using auto "
"hwaccel type %s with new default device.\n",
av_hwdevice_get_type_name(type));
}
}
if (dev) {
ist->hwaccel_device_type = type;
} else {
av_log(NULL, AV_LOG_INFO, "Auto hwaccel "
"disabled: no device found.\n");
ist->hwaccel_id = HWACCEL_NONE;
return 0;
}
}
if (!dev) {
av_log(NULL, AV_LOG_ERROR, "No device available "
"for decoder: device type %s needed for codec %s.\n",
av_hwdevice_get_type_name(type), ist->dec->name);
return err;
}
ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
if (!ist->dec_ctx->hw_device_ctx)
return AVERROR(ENOMEM);
return 0;
}
int hw_device_setup_for_encode(OutputStream *ost)
{
const AVCodecHWConfig *config;
HWDevice *dev = NULL;
AVBufferRef *frames_ref = NULL;
int i;
if (ost->filter) {
frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
if (frames_ref &&
((AVHWFramesContext*)frames_ref->data)->format ==
ost->enc_ctx->pix_fmt) {
// Matching format, will try to use hw_frames_ctx.
} else {
frames_ref = NULL;
}
}
for (i = 0;; i++) {
config = avcodec_get_hw_config(ost->enc_ctx->codec, i);
if (!config)
break;
if (frames_ref &&
config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX &&
(config->pix_fmt == AV_PIX_FMT_NONE ||
config->pix_fmt == ost->enc_ctx->pix_fmt)) {
av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input "
"frames context (format %s) with %s encoder.\n",
av_get_pix_fmt_name(ost->enc_ctx->pix_fmt),
ost->enc_ctx->codec->name);
ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref);
if (!ost->enc_ctx->hw_frames_ctx)
return AVERROR(ENOMEM);
return 0;
}
if (!dev &&
config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
dev = hw_device_get_by_type(config->device_type);
}
if (dev) {
av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using device %s "
"(type %s) with %s encoder.\n", dev->name,
av_hwdevice_get_type_name(dev->type), ost->enc_ctx->codec->name);
ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
if (!ost->enc_ctx->hw_device_ctx)
return AVERROR(ENOMEM);
} else {
// No device required, or no device available.
}
return 0;
}
static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input)
{
int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) {
InputStream *ist = avctx->opaque;
AVFrame *output = NULL;
enum AVPixelFormat output_format = ist->hwaccel_output_format;
@ -542,8 +337,10 @@ static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input)
err = av_hwframe_transfer_data(output, input, 0);
if (err < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to transfer data to "
"output frame: %d.\n", err);
av_log(avctx, AV_LOG_ERROR,
"Failed to transfer data to "
"output frame: %d.\n",
err);
goto fail;
}
@ -564,45 +361,26 @@ fail:
return err;
}
int hwaccel_decode_init(AVCodecContext *avctx)
{
InputStream *ist = avctx->opaque;
ist->hwaccel_retrieve_data = &hwaccel_retrieve_data;
return 0;
}
int hw_device_setup_for_filter(FilterGraph *fg)
{
HWDevice *dev;
int i;
AVBufferRef *hw_device_for_filter(void) {
// Pick the last hardware device if the user doesn't pick the device for
// filters explicitly with the filter_hw_device option.
if (filter_hw_device)
dev = filter_hw_device;
return filter_hw_device->device_ref;
else if (nb_hw_devices > 0) {
dev = hw_devices[nb_hw_devices - 1];
HWDevice *dev = hw_devices[nb_hw_devices - 1];
if (nb_hw_devices > 1)
av_log(NULL, AV_LOG_WARNING, "There are %d hardware devices. device "
"%s of type %s is picked for filters by default. Set hardware "
"device explicitly with the filter_hw_device option if device "
"%s is not usable for filters.\n",
nb_hw_devices, dev->name,
av_hwdevice_get_type_name(dev->type), dev->name);
} else
dev = NULL;
if (dev) {
for (i = 0; i < fg->graph->nb_filters; i++) {
fg->graph->filters[i]->hw_device_ctx =
av_buffer_ref(dev->device_ref);
if (!fg->graph->filters[i]->hw_device_ctx)
return AVERROR(ENOMEM);
}
av_log(
NULL, AV_LOG_WARNING,
"There are %d hardware devices. device "
"%s of type %s is picked for filters by default. Set hardware "
"device explicitly with the filter_hw_device option if device "
"%s is not usable for filters.\n",
nb_hw_devices, dev->name, av_hwdevice_get_type_name(dev->type),
dev->name);
return dev->device_ref;
}
return 0;
return NULL;
}

@ -1,7 +1,7 @@
/*
* This file is part of FFmpeg.
* Copyright (c) 2022 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
* Copyright (c) 2023-2024 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,12 +19,18 @@
*/
/*
* This file is the modified version of ffmpeg_mux.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of ffmpeg_mux.c file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop the ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
* - global thread local variables migrated to the mux->thread via FFmpegContext
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
@ -55,44 +61,38 @@
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "libavutil/timestamp.h"
#include "libavutil/thread.h"
#include "libavutil/timestamp.h"
#include "libavcodec/packet.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "ffmpeg_context.h"
__thread int want_sdp = 1;
MuxStream *ms_from_ost(OutputStream *ost)
{
return (MuxStream*)ost;
}
MuxStream *ms_from_ost(OutputStream *ost) { return (MuxStream *)ost; }
static Muxer *mux_from_of(OutputFile *of)
{
return (Muxer*)of;
}
static Muxer *mux_from_of(OutputFile *of) { return (Muxer *)of; }
static int64_t filesize(AVIOContext *pb)
{
static int64_t filesize(AVIOContext *pb) {
int64_t ret = -1;
if (pb) {
ret = avio_size(pb);
if (ret <= 0) // FIXME improve avio_size() so it works with non seekable output too
if (ret <= 0) // FIXME improve avio_size() so it works with non seekable
// output too
ret = avio_tell(pb);
}
return ret;
}
static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
{
static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) {
MuxStream *ms = ms_from_ost(ost);
AVFormatContext *s = mux->fc;
AVStream *st = ost->st;
int64_t fs;
uint64_t frame_num;
int ret;
@ -104,50 +104,68 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
goto fail;
}
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP)
if (ost->type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP)
pkt->pts = pkt->dts = AV_NOPTS_VALUE;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (ost->frame_rate.num && ost->is_cfr) {
if (pkt->duration > 0)
av_log(ost, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n");
pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate),
pkt->time_base);
}
}
av_packet_rescale_ts(pkt, pkt->time_base, ost->st->time_base);
// rescale timestamps to the stream timebase
if (ost->type == AVMEDIA_TYPE_AUDIO && !ost->enc) {
// use av_rescale_delta() for streamcopying audio, to preserve
// accuracy with coarse input timebases
int duration =
av_get_audio_frame_duration2(ost->st->codecpar, pkt->size);
if (!duration)
duration = ost->st->codecpar->frame_size;
pkt->dts = av_rescale_delta(
pkt->time_base, pkt->dts,
(AVRational){1, ost->st->codecpar->sample_rate}, duration,
&ms->ts_rescale_delta_last, ost->st->time_base);
pkt->pts = pkt->dts;
pkt->duration =
av_rescale_q(pkt->duration, pkt->time_base, ost->st->time_base);
} else
av_packet_rescale_ts(pkt, pkt->time_base, ost->st->time_base);
pkt->time_base = ost->st->time_base;
if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {
if (pkt->dts != AV_NOPTS_VALUE &&
pkt->pts != AV_NOPTS_VALUE &&
if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE &&
pkt->dts > pkt->pts) {
av_log(s, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64" in output stream %d:%d, replacing by guess\n",
pkt->dts, pkt->pts,
ost->file_index, ost->st->index);
pkt->pts =
pkt->dts = pkt->pts + pkt->dts + ms->last_mux_dts + 1
- FFMIN3(pkt->pts, pkt->dts, ms->last_mux_dts + 1)
- FFMAX3(pkt->pts, pkt->dts, ms->last_mux_dts + 1);
av_log(s, AV_LOG_WARNING,
"Invalid DTS: %" PRId64 " PTS: %" PRId64
" in output stream %d:%d, replacing by guess\n",
pkt->dts, pkt->pts, ost->file_index, ost->st->index);
pkt->pts = pkt->dts =
pkt->pts + pkt->dts + ms->last_mux_dts + 1 -
FFMIN3(pkt->pts, pkt->dts, ms->last_mux_dts + 1) -
FFMAX3(pkt->pts, pkt->dts, ms->last_mux_dts + 1);
}
if ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) &&
pkt->dts != AV_NOPTS_VALUE &&
ms->last_mux_dts != AV_NOPTS_VALUE) {
int64_t max = ms->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT);
if ((ost->type == AVMEDIA_TYPE_AUDIO ||
ost->type == AVMEDIA_TYPE_VIDEO ||
ost->type == AVMEDIA_TYPE_SUBTITLE) &&
pkt->dts != AV_NOPTS_VALUE && ms->last_mux_dts != AV_NOPTS_VALUE) {
int64_t max =
ms->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT);
if (pkt->dts < max) {
int loglevel = max - pkt->dts > 2 || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG;
int loglevel =
max - pkt->dts > 2 || ost->type == AVMEDIA_TYPE_VIDEO
? AV_LOG_WARNING
: AV_LOG_DEBUG;
if (exit_on_error)
loglevel = AV_LOG_ERROR;
av_log(s, loglevel, "Non-monotonous DTS in output stream "
"%d:%d; previous: %"PRId64", current: %"PRId64"; ",
ost->file_index, ost->st->index, ms->last_mux_dts, pkt->dts);
av_log(s, loglevel,
"Non-monotonic DTS in output stream "
"%d:%d; previous: %" PRId64 ", current: %" PRId64 "; ",
ost->file_index, ost->st->index, ms->last_mux_dts,
pkt->dts);
if (exit_on_error) {
ret = AVERROR(EINVAL);
goto fail;
}
av_log(s, loglevel, "changing to %"PRId64". This may result "
av_log(s, loglevel,
"changing to %" PRId64 ". This may result "
"in incorrect timestamps in the output file.\n",
max);
if (pkt->pts >= pkt->dts)
@ -158,20 +176,22 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
}
ms->last_mux_dts = pkt->dts;
ost->data_size_mux += pkt->size;
ms->data_size_mux += pkt->size;
frame_num = atomic_fetch_add(&ost->packets_written, 1);
pkt->stream_index = ost->index;
if (debug_ts) {
av_log(ost, AV_LOG_INFO, "muxer <- type:%s "
"pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d\n",
av_get_media_type_string(st->codecpar->codec_type),
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base),
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base),
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base),
pkt->size
);
av_log(
ost, AV_LOG_INFO,
"muxer <- type:%s "
"pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s "
"duration_time:%s size:%d\n",
av_get_media_type_string(ost->type), av_ts2str(pkt->pts),
av_ts2timestr(pkt->pts, &ost->st->time_base), av_ts2str(pkt->dts),
av_ts2timestr(pkt->dts, &ost->st->time_base),
av_ts2str(pkt->duration),
av_ts2timestr(pkt->duration, &ost->st->time_base), pkt->size);
}
if (ms->stats.io)
@ -179,7 +199,8 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
ret = av_interleaved_write_frame(s, pkt);
if (ret < 0) {
print_error("av_interleaved_write_frame()", ret);
av_log(ost, AV_LOG_ERROR,
"Error submitting a packet to the muxer: %s\n", av_err2str(ret));
goto fail;
}
@ -189,8 +210,8 @@ fail:
return ret;
}
static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int *stream_eof)
{
static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt,
int *stream_eof) {
OutputFile *of = &mux->of;
if (ost->sq_idx_mux >= 0) {
@ -204,11 +225,16 @@ static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int
while (1) {
ret = sq_receive(mux->sq_mux, -1, SQPKT(mux->sq_pkt));
if (ret < 0)
return (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) ? 0 : ret;
if (ret < 0) {
/* n.b.: We forward EOF from the sync queue, terminating muxing.
* This assumes that if a muxing sync queue is present, then all
* the streams use it. That is true currently, but may change in
* the future, then this code needs to be revisited.
*/
return ret == AVERROR(EAGAIN) ? 0 : ret;
}
ret = write_packet(mux, of->streams[ret],
mux->sq_pkt);
ret = write_packet(mux, of->streams[ret], mux->sq_pkt);
if (ret < 0)
return ret;
}
@ -218,19 +244,21 @@ static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int
return 0;
}
static void thread_set_name(OutputFile *of)
{
static void thread_set_name(OutputFile *of) {
char name[16];
snprintf(name, sizeof(name), "mux%d:%s", of->index, of->format->name);
ff_thread_setname(name);
}
static void *muxer_thread(void *arg)
{
Muxer *mux = arg;
static void *muxer_thread(void *arg) {
FFmpegContext *context = (FFmpegContext *)arg;
loadFFmpegContext(context);
Muxer *mux = (Muxer *)context->arg;
av_free(arg);
OutputFile *of = &mux->of;
AVPacket *pkt = NULL;
int ret = 0;
AVPacket *pkt = NULL;
int ret = 0;
pkt = av_packet_alloc();
if (!pkt) {
@ -254,9 +282,15 @@ static void *muxer_thread(void *arg)
ost = of->streams[stream_idx];
ret = sync_queue_process(mux, ost, ret < 0 ? NULL : pkt, &stream_eof);
av_packet_unref(pkt);
if (ret == AVERROR_EOF && stream_eof)
tq_receive_finish(mux->tq, stream_idx);
else if (ret < 0) {
if (ret == AVERROR_EOF) {
if (stream_eof) {
tq_receive_finish(mux->tq, stream_idx);
} else {
av_log(mux, AV_LOG_VERBOSE, "Muxer returned EOF\n");
ret = 0;
break;
}
} else if (ret < 0) {
av_log(mux, AV_LOG_ERROR, "Error muxing a packet\n");
break;
}
@ -270,11 +304,10 @@ finish:
av_log(mux, AV_LOG_VERBOSE, "Terminating muxer thread\n");
return (void*)(intptr_t)ret;
return (void *)(intptr_t)ret;
}
static int thread_submit_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
{
static int thread_submit_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) {
int ret = 0;
if (!pkt || ost->finished & MUXER_FINISHED)
@ -295,8 +328,7 @@ finish:
return ret == AVERROR_EOF ? 0 : ret;
}
static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
{
static int queue_packet(OutputStream *ost, AVPacket *pkt) {
MuxStream *ms = ms_from_ost(ost);
AVPacket *tmp_pkt = NULL;
int ret;
@ -305,8 +337,9 @@ static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
size_t cur_size = av_fifo_can_read(ms->muxing_queue);
size_t pkt_size = pkt ? pkt->size : 0;
unsigned int are_we_over_size =
(ms->muxing_queue_data_size + pkt_size) > ms->muxing_queue_data_threshold;
size_t limit = are_we_over_size ? ms->max_muxing_queue_size : SIZE_MAX;
(ms->muxing_queue_data_size + pkt_size) >
ms->muxing_queue_data_threshold;
size_t limit = are_we_over_size ? ms->max_muxing_queue_size : SIZE_MAX;
size_t new_size = FFMIN(2 * cur_size, limit);
if (new_size <= cur_size) {
@ -337,15 +370,14 @@ static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
return 0;
}
static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost)
{
static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost) {
int ret;
if (mux->tq) {
return thread_submit_packet(mux, ost, pkt);
} else {
/* the muxer is not initialized yet, buffer the packet */
ret = queue_packet(mux, ost, pkt);
ret = queue_packet(ost, pkt);
if (ret < 0) {
if (pkt)
av_packet_unref(pkt);
@ -356,30 +388,34 @@ static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost)
return 0;
}
void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof)
{
int of_output_packet(OutputFile *of, OutputStream *ost, AVPacket *pkt) {
Muxer *mux = mux_from_of(of);
MuxStream *ms = ms_from_ost(ost);
const char *err_msg;
int ret = 0;
if (!eof && pkt->dts != AV_NOPTS_VALUE)
ost->last_mux_dts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q);
if (pkt && pkt->dts != AV_NOPTS_VALUE)
ost->last_mux_dts =
av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q);
/* apply the output bitstream filters */
if (ms->bsf_ctx) {
int bsf_eof = 0;
ret = av_bsf_send_packet(ms->bsf_ctx, eof ? NULL : pkt);
if (pkt)
av_packet_rescale_ts(pkt, pkt->time_base,
ms->bsf_ctx->time_base_in);
ret = av_bsf_send_packet(ms->bsf_ctx, pkt);
if (ret < 0) {
err_msg = "submitting a packet for bitstream filtering";
goto fail;
}
while (!bsf_eof) {
ret = av_bsf_receive_packet(ms->bsf_ctx, pkt);
ret = av_bsf_receive_packet(ms->bsf_ctx, ms->bsf_pkt);
if (ret == AVERROR(EAGAIN))
return;
return 0;
else if (ret == AVERROR_EOF)
bsf_eof = 1;
else if (ret < 0) {
@ -387,30 +423,100 @@ void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof)
goto fail;
}
ret = submit_packet(mux, bsf_eof ? NULL : pkt, ost);
if (!bsf_eof)
ms->bsf_pkt->time_base = ms->bsf_ctx->time_base_out;
ret = submit_packet(mux, bsf_eof ? NULL : ms->bsf_pkt, ost);
if (ret < 0)
goto mux_fail;
}
} else {
ret = submit_packet(mux, eof ? NULL : pkt, ost);
ret = submit_packet(mux, pkt, ost);
if (ret < 0)
goto mux_fail;
}
return;
return 0;
mux_fail:
err_msg = "submitting a packet to the muxer";
fail:
av_log(ost, AV_LOG_ERROR, "Error %s\n", err_msg);
if (exit_on_error)
exit_program(1);
return exit_on_error ? ret : 0;
}
int of_streamcopy(OutputStream *ost, const AVPacket *pkt, int64_t dts) {
OutputFile *of = output_files[ost->file_index];
MuxStream *ms = ms_from_ost(ost);
int64_t start_time =
(of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time;
int64_t ts_offset;
AVPacket *opkt = ms->pkt;
int ret;
av_packet_unref(opkt);
if (of->recording_time != INT64_MAX &&
dts >= of->recording_time + start_time)
pkt = NULL;
// EOF: flush output bitstream filters.
if (!pkt)
return of_output_packet(of, ost, NULL);
if (!ms->streamcopy_started && !(pkt->flags & AV_PKT_FLAG_KEY) &&
!ms->copy_initial_nonkeyframes)
return 0;
if (!ms->streamcopy_started) {
if (!ms->copy_prior_start &&
(pkt->pts == AV_NOPTS_VALUE
? dts < ms->ts_copy_start
: pkt->pts < av_rescale_q(ms->ts_copy_start, AV_TIME_BASE_Q,
pkt->time_base)))
return 0;
if (of->start_time != AV_NOPTS_VALUE && dts < of->start_time)
return 0;
}
ret = av_packet_ref(opkt, pkt);
if (ret < 0)
return ret;
ts_offset = av_rescale_q(start_time, AV_TIME_BASE_Q, opkt->time_base);
if (pkt->pts != AV_NOPTS_VALUE)
opkt->pts -= ts_offset;
if (pkt->dts == AV_NOPTS_VALUE) {
opkt->dts = av_rescale_q(dts, AV_TIME_BASE_Q, opkt->time_base);
} else if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
opkt->pts = opkt->dts - ts_offset;
}
opkt->dts -= ts_offset;
{
int ret = trigger_fix_sub_duration_heartbeat(ost, pkt);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR,
"Subtitle heartbeat logic failed in %s! (%s)\n", __func__,
av_err2str(ret));
return ret;
}
}
ret = of_output_packet(of, ost, opkt);
if (ret < 0)
return ret;
ms->streamcopy_started = 1;
return 0;
}
static int thread_stop(Muxer *mux)
{
static int thread_stop(Muxer *mux) {
void *ret;
if (!mux || !mux->tq)
@ -426,13 +532,7 @@ static int thread_stop(Muxer *mux)
return (int)(intptr_t)ret;
}
static void pkt_move(void *dst, void *src)
{
av_packet_move_ref(dst, src);
}
static int thread_start(Muxer *mux)
{
static int thread_start(Muxer *mux) {
AVFormatContext *fc = mux->fc;
ObjPool *op;
int ret;
@ -447,7 +547,10 @@ static int thread_start(Muxer *mux)
return AVERROR(ENOMEM);
}
ret = pthread_create(&mux->thread, NULL, muxer_thread, (void*)mux);
FFmpegContext *context = saveFFmpegContext();
context->arg = mux;
ret = pthread_create(&mux->thread, NULL, muxer_thread, context);
if (ret) {
tq_free(&mux->tq);
return AVERROR(ret);
@ -456,13 +559,9 @@ static int thread_start(Muxer *mux)
/* flush the muxing queues */
for (int i = 0; i < fc->nb_streams; i++) {
OutputStream *ost = mux->of.streams[i];
MuxStream *ms = ms_from_ost(ost);
MuxStream *ms = ms_from_ost(ost);
AVPacket *pkt;
/* try to improve muxing time_base (only possible if nothing has been written yet) */
if (!av_fifo_can_read(ms->muxing_queue))
ost->mux_timebase = ost->st->time_base;
while (av_fifo_read(ms->muxing_queue, &pkt, 1) >= 0) {
ret = thread_submit_packet(mux, ost, pkt);
if (pkt) {
@ -477,8 +576,7 @@ static int thread_start(Muxer *mux)
return 0;
}
static int print_sdp(void)
{
static int print_sdp(void) {
char sdp[16384];
int i;
int j, ret;
@ -516,7 +614,8 @@ static int print_sdp(void)
} else {
ret = avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", sdp_filename);
av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n",
sdp_filename);
goto fail;
}
@ -533,8 +632,7 @@ fail:
return ret;
}
int mux_check_init(Muxer *mux)
{
int mux_check_init(Muxer *mux) {
OutputFile *of = &mux->of;
AVFormatContext *fc = mux->fc;
int ret, i;
@ -547,11 +645,13 @@ int mux_check_init(Muxer *mux)
ret = avformat_write_header(fc, &mux->opts);
if (ret < 0) {
av_log(mux, AV_LOG_ERROR, "Could not write header (incorrect codec "
"parameters ?): %s\n", av_err2str(ret));
av_log(mux, AV_LOG_ERROR,
"Could not write header (incorrect codec "
"parameters ?): %s\n",
av_err2str(ret));
return ret;
}
//assert_avoptions(of->opts);
// assert_avoptions(of->opts);
mux->header_written = 1;
av_dump_format(fc, of->index, fc->url, 1);
@ -580,16 +680,15 @@ int mux_check_init(Muxer *mux)
return 0;
}
static int bsf_init(MuxStream *ms)
{
static int bsf_init(MuxStream *ms) {
OutputStream *ost = &ms->ost;
AVBSFContext *ctx = ms->bsf_ctx;
int ret;
if (!ctx)
return 0;
return avcodec_parameters_copy(ost->st->codecpar, ost->par_in);
ret = avcodec_parameters_copy(ctx->par_in, ost->st->codecpar);
ret = avcodec_parameters_copy(ctx->par_in, ost->par_in);
if (ret < 0)
return ret;
@ -607,18 +706,18 @@ static int bsf_init(MuxStream *ms)
return ret;
ost->st->time_base = ctx->time_base_out;
ms->bsf_pkt = av_packet_alloc();
if (!ms->bsf_pkt)
return AVERROR(ENOMEM);
return 0;
}
int of_stream_init(OutputFile *of, OutputStream *ost)
{
int of_stream_init(OutputFile *of, OutputStream *ost) {
Muxer *mux = mux_from_of(of);
MuxStream *ms = ms_from_ost(ost);
int ret;
if (ost->sq_idx_mux >= 0)
sq_set_tb(mux->sq_mux, ost->sq_idx_mux, ost->mux_timebase);
/* initialize bitstream filters for the output stream
* needs to be done here, because the codec id for streamcopy is not
* known until now */
@ -626,16 +725,130 @@ int of_stream_init(OutputFile *of, OutputStream *ost)
if (ret < 0)
return ret;
if (ms->stream_duration) {
ost->st->duration = av_rescale_q(
ms->stream_duration, ms->stream_duration_tb, ost->st->time_base);
}
ost->initialized = 1;
return mux_check_init(mux);
}
int of_write_trailer(OutputFile *of)
{
static int check_written(OutputFile *of) {
int64_t total_packets_written = 0;
int pass1_used = 1;
int ret = 0;
for (int i = 0; i < of->nb_streams; i++) {
OutputStream *ost = of->streams[i];
uint64_t packets_written = atomic_load(&ost->packets_written);
total_packets_written += packets_written;
if (ost->enc_ctx && (ost->enc_ctx->flags &
(AV_CODEC_FLAG_PASS1 | AV_CODEC_FLAG_PASS2)) !=
AV_CODEC_FLAG_PASS1)
pass1_used = 0;
if (!packets_written &&
(abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM)) {
av_log(ost, AV_LOG_FATAL, "Empty output stream\n");
ret = err_merge(ret, AVERROR(EINVAL));
}
}
if (!total_packets_written) {
int level = AV_LOG_WARNING;
if (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT) {
ret = err_merge(ret, AVERROR(EINVAL));
level = AV_LOG_FATAL;
}
av_log(of, level, "Output file is empty, nothing was encoded%s\n",
pass1_used ? ""
: "(check -ss / -t / -frames parameters if used)");
}
return ret;
}
static void mux_final_stats(Muxer *mux) {
OutputFile *of = &mux->of;
uint64_t total_packets = 0, total_size = 0;
uint64_t video_size = 0, audio_size = 0, subtitle_size = 0, extra_size = 0,
other_size = 0;
uint8_t overhead[16] = "unknown";
int64_t file_size = of_filesize(of);
av_log(of, AV_LOG_VERBOSE, "Output file #%d (%s):\n", of->index, of->url);
for (int j = 0; j < of->nb_streams; j++) {
OutputStream *ost = of->streams[j];
MuxStream *ms = ms_from_ost(ost);
const AVCodecParameters *par = ost->st->codecpar;
const enum AVMediaType type = par->codec_type;
const uint64_t s = ms->data_size_mux;
switch (type) {
case AVMEDIA_TYPE_VIDEO:
video_size += s;
break;
case AVMEDIA_TYPE_AUDIO:
audio_size += s;
break;
case AVMEDIA_TYPE_SUBTITLE:
subtitle_size += s;
break;
default:
other_size += s;
break;
}
extra_size += par->extradata_size;
total_size += s;
total_packets += atomic_load(&ost->packets_written);
av_log(of, AV_LOG_VERBOSE, " Output stream #%d:%d (%s): ", of->index,
j, av_get_media_type_string(type));
if (ost->enc) {
av_log(of, AV_LOG_VERBOSE, "%" PRIu64 " frames encoded",
ost->frames_encoded);
if (type == AVMEDIA_TYPE_AUDIO)
av_log(of, AV_LOG_VERBOSE, " (%" PRIu64 " samples)",
ost->samples_encoded);
av_log(of, AV_LOG_VERBOSE, "; ");
}
av_log(of, AV_LOG_VERBOSE,
"%" PRIu64 " packets muxed (%" PRIu64 " bytes); ",
atomic_load(&ost->packets_written), s);
av_log(of, AV_LOG_VERBOSE, "\n");
}
av_log(of, AV_LOG_VERBOSE,
" Total: %" PRIu64 " packets (%" PRIu64 " bytes) muxed\n",
total_packets, total_size);
if (total_size && file_size > 0 && file_size >= total_size) {
snprintf(overhead, sizeof(overhead), "%f%%",
100.0 * (file_size - total_size) / total_size);
}
av_log(of, AV_LOG_INFO,
"video:%1.0fkB audio:%1.0fkB subtitle:%1.0fkB other streams:%1.0fkB "
"global headers:%1.0fkB muxing overhead: %s\n",
video_size / 1024.0, audio_size / 1024.0, subtitle_size / 1024.0,
other_size / 1024.0, extra_size / 1024.0, overhead);
}
int of_write_trailer(OutputFile *of) {
Muxer *mux = mux_from_of(of);
AVFormatContext *fc = mux->fc;
int ret;
int ret, mux_result = 0;
if (!mux->tq) {
av_log(mux, AV_LOG_ERROR,
@ -644,14 +857,13 @@ int of_write_trailer(OutputFile *of)
return AVERROR(EINVAL);
}
ret = thread_stop(mux);
if (ret < 0)
main_ffmpeg_return_code = ret;
mux_result = thread_stop(mux);
ret = av_write_trailer(fc);
if (ret < 0) {
av_log(mux, AV_LOG_ERROR, "Error writing trailer: %s\n", av_err2str(ret));
return ret;
av_log(mux, AV_LOG_ERROR, "Error writing trailer: %s\n",
av_err2str(ret));
mux_result = err_merge(mux_result, ret);
}
mux->last_filesize = filesize(fc->pb);
@ -659,16 +871,22 @@ int of_write_trailer(OutputFile *of)
if (!(of->format->flags & AVFMT_NOFILE)) {
ret = avio_closep(&fc->pb);
if (ret < 0) {
av_log(mux, AV_LOG_ERROR, "Error closing file: %s\n", av_err2str(ret));
return ret;
av_log(mux, AV_LOG_ERROR, "Error closing file: %s\n",
av_err2str(ret));
mux_result = err_merge(mux_result, ret);
}
}
return 0;
mux_final_stats(mux);
// check whether anything was actually written
ret = check_written(of);
mux_result = err_merge(mux_result, ret);
return mux_result;
}
static void ost_free(OutputStream **post)
{
static void ost_free(OutputStream **post) {
OutputStream *ost = *post;
MuxStream *ms;
@ -676,6 +894,8 @@ static void ost_free(OutputStream **post)
return;
ms = ms_from_ost(ost);
enc_free(&ost->enc);
if (ost->logfile) {
if (fclose(ost->logfile))
av_log(ms, AV_LOG_ERROR,
@ -691,18 +911,17 @@ static void ost_free(OutputStream **post)
av_fifo_freep2(&ms->muxing_queue);
}
avcodec_parameters_free(&ost->par_in);
av_bsf_free(&ms->bsf_ctx);
av_packet_free(&ms->bsf_pkt);
av_frame_free(&ost->filtered_frame);
av_frame_free(&ost->sq_frame);
av_frame_free(&ost->last_frame);
av_packet_free(&ost->pkt);
av_packet_free(&ms->pkt);
av_dict_free(&ost->encoder_opts);
av_freep(&ost->kf.pts);
av_expr_free(ost->kf.pexpr);
av_freep(&ost->avfilter);
av_freep(&ost->logfile_prefix);
av_freep(&ost->apad);
@ -733,8 +952,7 @@ static void ost_free(OutputStream **post)
av_freep(post);
}
static void fc_close(AVFormatContext **pfc)
{
static void fc_close(AVFormatContext **pfc) {
AVFormatContext *fc = *pfc;
if (!fc)
@ -747,8 +965,7 @@ static void fc_close(AVFormatContext **pfc)
*pfc = NULL;
}
void of_close(OutputFile **pof)
{
void of_free(OutputFile **pof) {
OutputFile *of = *pof;
Muxer *mux;
@ -774,8 +991,7 @@ void of_close(OutputFile **pof)
av_freep(pof);
}
int64_t of_filesize(OutputFile *of)
{
int64_t of_filesize(OutputFile *of) {
Muxer *mux = mux_from_of(of);
return atomic_load(&mux->last_filesize);
}

@ -1,6 +1,6 @@
/*
* Muxer internal APIs - should not be included outside of ffmpeg_mux*
* Copyright (c) 2023 ARTHENICA LTD
* Copyright (c) 2023-2024 ARTHENICA LTD
*
* This file is part of FFmpeg.
*
@ -20,20 +20,25 @@
*/
/*
* This file is the modified version of ffmpeg_mux.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of ffmpeg_mux.h file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - fftools header names updated
* - want_sdp made thread-local
* - EncStatsFile declaration migrated from ffmpeg_mux_init.c
* - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT, SPECIFIER_OPT_FMT declarations migrated from
* ffmpeg.h
* - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT,
* SPECIFIER_OPT_FMT declarations migrated from ffmpeg.h
* - ms_from_ost migrated to ffmpeg_mux.c
*/
@ -53,49 +58,58 @@
#include "libavutil/fifo.h"
#include "libavutil/thread.h"
#define SPECIFIER_OPT_FMT_str "%s"
#define SPECIFIER_OPT_FMT_i "%i"
#define SPECIFIER_OPT_FMT_i64 "%"PRId64
#define SPECIFIER_OPT_FMT_ui64 "%"PRIu64
#define SPECIFIER_OPT_FMT_f "%f"
#define SPECIFIER_OPT_FMT_dbl "%lf"
#define WARN_MULTIPLE_OPT_USAGE(name, type, so, st)\
{\
char namestr[128] = "";\
const char *spec = so->specifier && so->specifier[0] ? so->specifier : "";\
for (int _i = 0; opt_name_##name[_i]; _i++)\
av_strlcatf(namestr, sizeof(namestr), "-%s%s", opt_name_##name[_i], opt_name_##name[_i+1] ? (opt_name_##name[_i+2] ? ", " : " or ") : "");\
av_log(NULL, AV_LOG_WARNING, "Multiple %s options specified for stream %d, only the last option '-%s%s%s "SPECIFIER_OPT_FMT_##type"' will be used.\n",\
namestr, st->index, opt_name_##name[0], spec[0] ? ":" : "", spec, so->u.type);\
}
#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
{\
int _ret, _matches = 0;\
SpecifierOpt *so;\
for (int _i = 0; _i < o->nb_ ## name; _i++) {\
char *spec = o->name[_i].specifier;\
if ((_ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\
outvar = o->name[_i].u.type;\
so = &o->name[_i];\
_matches++;\
} else if (_ret < 0)\
exit_program(1);\
}\
if (_matches > 1)\
WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\
}
#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\
{\
int i;\
for (i = 0; i < o->nb_ ## name; i++) {\
char *spec = o->name[i].specifier;\
if (!strcmp(spec, mediatype))\
outvar = o->name[i].u.type;\
}\
}
#define SPECIFIER_OPT_FMT_str "%s"
#define SPECIFIER_OPT_FMT_i "%i"
#define SPECIFIER_OPT_FMT_i64 "%" PRId64
#define SPECIFIER_OPT_FMT_ui64 "%" PRIu64
#define SPECIFIER_OPT_FMT_f "%f"
#define SPECIFIER_OPT_FMT_dbl "%lf"
#define WARN_MULTIPLE_OPT_USAGE(name, type, so, st) \
{ \
char namestr[128] = ""; \
const char *spec = \
so->specifier && so->specifier[0] ? so->specifier : ""; \
for (int _i = 0; opt_name_##name[_i]; _i++) \
av_strlcatf(namestr, sizeof(namestr), "-%s%s", \
opt_name_##name[_i], \
opt_name_##name[_i + 1] \
? (opt_name_##name[_i + 2] ? ", " : " or ") \
: ""); \
av_log(NULL, AV_LOG_WARNING, \
"Multiple %s options specified for stream %d, only the last " \
"option '-%s%s%s " SPECIFIER_OPT_FMT_##type \
"' will be used.\n", \
namestr, st->index, opt_name_##name[0], spec[0] ? ":" : "", \
spec, so->u.type); \
}
#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st) \
{ \
int _ret, _matches = 0; \
SpecifierOpt *so; \
for (int _i = 0; _i < o->nb_##name; _i++) { \
char *spec = o->name[_i].specifier; \
if ((_ret = check_stream_specifier(fmtctx, st, spec)) > 0) { \
outvar = o->name[_i].u.type; \
so = &o->name[_i]; \
_matches++; \
} else if (_ret < 0) \
return _ret; \
} \
if (_matches > 1) \
WARN_MULTIPLE_OPT_USAGE(name, type, so, st); \
}
#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype) \
{ \
int i; \
for (i = 0; i < o->nb_##name; i++) { \
char *spec = o->name[i].specifier; \
if (!strcmp(spec, mediatype)) \
outvar = o->name[i].u.type; \
} \
}
typedef struct MuxStream {
OutputStream ost;
@ -103,10 +117,14 @@ typedef struct MuxStream {
// name used for logging
char log_name[32];
/* the packets are buffered here until the muxer is ready to be initialized */
/* the packets are buffered here until the muxer is ready to be initialized
*/
AVFifo *muxing_queue;
AVBSFContext *bsf_ctx;
AVPacket *bsf_pkt;
AVPacket *pkt;
EncStats stats;
@ -123,9 +141,27 @@ typedef struct MuxStream {
/* Threshold after which max_muxing_queue_size will be in effect */
size_t muxing_queue_data_threshold;
// timestamp from which the streamcopied streams should start,
// in AV_TIME_BASE_Q;
// everything before it should be discarded
int64_t ts_copy_start;
/* dts of the last packet sent to the muxer, in the stream timebase
* used for making up missing dts values */
int64_t last_mux_dts;
int64_t stream_duration;
AVRational stream_duration_tb;
// state for av_rescale_delta() call for audio in write_packet()
int64_t ts_rescale_delta_last;
// combined size of all the packets sent to the muxer
uint64_t data_size_mux;
int copy_initial_nonkeyframes;
int copy_prior_start;
int streamcopy_started;
} MuxStream;
typedef struct Muxer {
@ -136,7 +172,7 @@ typedef struct Muxer {
AVFormatContext *fc;
pthread_t thread;
pthread_t thread;
ThreadQueue *tq;
AVDictionary *opts;
@ -152,11 +188,6 @@ typedef struct Muxer {
AVPacket *sq_pkt;
} Muxer;
typedef struct EncStatsFile {
char *path;
AVIOContext *io;
} EncStatsFile;
/* whether we want to print an SDP, set in of_open() */
extern __thread int want_sdp;

@ -17,8 +17,9 @@
*/
/*
* This file is the modified version of fopen_utf8.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of fopen_utf8.h file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop the ffmpeg-kit library.
*
* ffmpeg-kit changes by Taner Sener
@ -37,13 +38,13 @@
#ifdef _WIN32
#include "libavutil/wchar_filename.h"
static inline FILE *fopen_utf8(const char *path_utf8, const char *mode)
{
static inline FILE *fopen_utf8(const char *path_utf8, const char *mode) {
wchar_t *path_w, *mode_w;
FILE *f;
/* convert UTF-8 to wide chars */
if (get_extended_win32_path(path_utf8, &path_w)) /* This sets errno on error. */
if (get_extended_win32_path(path_utf8,
&path_w)) /* This sets errno on error. */
return NULL;
if (!path_w)
goto fallback;
@ -70,8 +71,7 @@ fallback:
#else
static inline FILE *fopen_utf8(const char *path, const char *mode)
{
static inline FILE *fopen_utf8(const char *path, const char *mode) {
return fopen(path, mode);
}
#endif

@ -18,9 +18,10 @@
*/
/*
* This file is the modified version of objpool.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
* This file is the modified version of objpool.c file living in ffmpeg source
* code under the fftools folder. We manually update it each time we depend on a
* new ffmpeg version. Below you can see the list of changes applied by us to
* develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
@ -42,17 +43,16 @@
#include "fftools_objpool.h"
struct ObjPool {
void *pool[32];
void *pool[32];
unsigned int pool_count;
ObjPoolCBAlloc alloc;
ObjPoolCBReset reset;
ObjPoolCBFree free;
ObjPoolCBFree free;
};
ObjPool *objpool_alloc(ObjPoolCBAlloc cb_alloc, ObjPoolCBReset cb_reset,
ObjPoolCBFree cb_free)
{
ObjPoolCBFree cb_free) {
ObjPool *op = av_mallocz(sizeof(*op));
if (!op)
@ -60,13 +60,12 @@ ObjPool *objpool_alloc(ObjPoolCBAlloc cb_alloc, ObjPoolCBReset cb_reset,
op->alloc = cb_alloc;
op->reset = cb_reset;
op->free = cb_free;
op->free = cb_free;
return op;
}
void objpool_free(ObjPool **pop)
{
void objpool_free(ObjPool **pop) {
ObjPool *op = *pop;
if (!op)
@ -78,8 +77,7 @@ void objpool_free(ObjPool **pop)
av_freep(pop);
}
int objpool_get(ObjPool *op, void **obj)
{
int objpool_get(ObjPool *op, void **obj) {
if (op->pool_count) {
*obj = op->pool[--op->pool_count];
op->pool[op->pool_count] = NULL;
@ -89,8 +87,7 @@ int objpool_get(ObjPool *op, void **obj)
return *obj ? 0 : AVERROR(ENOMEM);
}
void objpool_release(ObjPool *op, void **obj)
{
void objpool_release(ObjPool *op, void **obj) {
if (!*obj)
return;
@ -104,42 +101,26 @@ void objpool_release(ObjPool *op, void **obj)
*obj = NULL;
}
static void *alloc_packet(void)
{
return av_packet_alloc();
}
static void *alloc_frame(void)
{
return av_frame_alloc();
}
static void *alloc_packet(void) { return av_packet_alloc(); }
static void *alloc_frame(void) { return av_frame_alloc(); }
static void reset_packet(void *obj)
{
av_packet_unref(obj);
}
static void reset_frame(void *obj)
{
av_frame_unref(obj);
}
static void reset_packet(void *obj) { av_packet_unref(obj); }
static void reset_frame(void *obj) { av_frame_unref(obj); }
static void free_packet(void **obj)
{
static void free_packet(void **obj) {
AVPacket *pkt = *obj;
av_packet_free(&pkt);
*obj = NULL;
}
static void free_frame(void **obj)
{
static void free_frame(void **obj) {
AVFrame *frame = *obj;
av_frame_free(&frame);
*obj = NULL;
}
ObjPool *objpool_alloc_packets(void)
{
ObjPool *objpool_alloc_packets(void) {
return objpool_alloc(alloc_packet, reset_packet, free_packet);
}
ObjPool *objpool_alloc_frames(void)
{
ObjPool *objpool_alloc_frames(void) {
return objpool_alloc(alloc_frame, reset_frame, free_frame);
}

@ -18,9 +18,10 @@
*/
/*
* This file is the modified version of objpool.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
* This file is the modified version of objpool.h file living in ffmpeg source
* code under the fftools folder. We manually update it each time we depend on a
* new ffmpeg version. Below you can see the list of changes applied by us to
* develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
@ -34,17 +35,17 @@
typedef struct ObjPool ObjPool;
typedef void* (*ObjPoolCBAlloc)(void);
typedef void (*ObjPoolCBReset)(void *);
typedef void (*ObjPoolCBFree)(void **);
typedef void *(*ObjPoolCBAlloc)(void);
typedef void (*ObjPoolCBReset)(void *);
typedef void (*ObjPoolCBFree)(void **);
void objpool_free(ObjPool **op);
void objpool_free(ObjPool **op);
ObjPool *objpool_alloc(ObjPoolCBAlloc cb_alloc, ObjPoolCBReset cb_reset,
ObjPoolCBFree cb_free);
ObjPool *objpool_alloc_packets(void);
ObjPool *objpool_alloc_frames(void);
int objpool_get(ObjPool *op, void **obj);
int objpool_get(ObjPool *op, void **obj);
void objpool_release(ObjPool *op, void **obj);
#endif // FFTOOLS_OBJPOOL_H

@ -20,15 +20,17 @@
*/
/*
* This file is the modified version of opt_common.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of opt_common.h file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop the ffmpeg-kit library.
*
* ffmpeg-kit changes by Taner Sener
*
* 09.2022
* --------------------------------------------------------
* - CMDUTILS_COMMON_OPTIONS and CMDUTILS_COMMON_OPTIONS_AVDEVICE defines dropped
* - CMDUTILS_COMMON_OPTIONS and CMDUTILS_COMMON_OPTIONS_AVDEVICE defines
* dropped
* - fftools_ prefix added to fftools headers
*/

@ -1,6 +1,6 @@
/*
* This file is part of FFmpeg.
* Copyright (c) 2023 ARTHENICA LTD
* Copyright (c) 2023-2024 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,12 +18,17 @@
*/
/*
* This file is the modified version of sync_queue.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of sync_queue.c file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
@ -34,31 +39,76 @@
#include <string.h>
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/cpu.h"
#include "libavutil/error.h"
#include "libavutil/fifo.h"
#include "libavutil/mathematics.h"
#include "libavutil/mem.h"
#include "libavutil/samplefmt.h"
#include "libavutil/timestamp.h"
#include "fftools_objpool.h"
#include "fftools_sync_queue.h"
/*
* How this works:
* --------------
* time: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
* -------------------------------------------------------------------
* | | | | | | | | | | | | | |
* |
* stream 0| d=1 d=2 d=1 d=3
* |
*
* stream 1d=1 d=5
*
* |
* stream 2| d=1d=1d=1d=1 <- stream 2 is the head stream of the queue
* |
* ^ ^
* [stream 2 tail] [stream 2 head]
*
* We have N streams (N=3 in the diagram), each stream is a FIFO. The *tail* of
* each FIFO is the frame with smallest end time, the *head* is the frame with
* the largest end time. Frames submitted to the queue with sq_send() are placed
* after the head, frames returned to the caller with sq_receive() are taken
* from the tail.
*
* The head stream of the whole queue (SyncQueue.head_stream) is the limiting
* stream with the *smallest* head timestamp, i.e. the stream whose source lags
* furthest behind all other streams. It determines which frames can be output
* from the queue.
*
* In the diagram, the head stream is 2, because it head time is t=5, while
* streams 0 and 1 end at t=8 and t=9 respectively. All frames that _end_ at
* or before t=5 can be output, i.e. the first 3 frames from stream 0, first
* frame from stream 1, and all 4 frames from stream 2.
*/
typedef struct SyncQueueStream {
AVFifo *fifo;
AVRational tb;
AVFifo *fifo;
AVRational tb;
/* number of audio samples in fifo */
uint64_t samples_queued;
/* stream head: largest timestamp seen */
int64_t head_ts;
int limiting;
int64_t head_ts;
int limiting;
/* no more frames will be sent for this stream */
int finished;
int finished;
uint64_t frames_sent;
uint64_t frames_max;
uint64_t frames_sent;
uint64_t samples_sent;
uint64_t frames_max;
int frame_samples;
} SyncQueueStream;
struct SyncQueue {
enum SyncQueueType type;
void *logctx;
/* no more frames will be sent for any stream */
int finished;
/* sync head: the stream with the _smallest_ head timestamp
@ -71,37 +121,76 @@ struct SyncQueue {
int64_t buf_size_us;
SyncQueueStream *streams;
unsigned int nb_streams;
unsigned int nb_streams;
// pool of preallocated frames to avoid constant allocations
ObjPool *pool;
int have_limiting;
uintptr_t align_mask;
};
static void frame_move(const SyncQueue *sq, SyncQueueFrame dst,
SyncQueueFrame src)
{
SyncQueueFrame src) {
if (sq->type == SYNC_QUEUE_PACKETS)
av_packet_move_ref(dst.p, src.p);
else
av_frame_move_ref(dst.f, src.f);
}
static int64_t frame_ts(const SyncQueue *sq, SyncQueueFrame frame)
{
return (sq->type == SYNC_QUEUE_PACKETS) ?
frame.p->pts + frame.p->duration :
frame.f->pts + frame.f->duration;
/**
* Compute the end timestamp of a frame. If nb_samples is provided, consider
* the frame to have this number of audio samples, otherwise use frame duration.
*/
static int64_t frame_end(const SyncQueue *sq, SyncQueueFrame frame,
int nb_samples) {
if (nb_samples) {
int64_t d =
av_rescale_q(nb_samples, (AVRational){1, frame.f->sample_rate},
frame.f->time_base);
return frame.f->pts + d;
}
return (sq->type == SYNC_QUEUE_PACKETS) ? frame.p->pts + frame.p->duration
: frame.f->pts + frame.f->duration;
}
static int frame_samples(const SyncQueue *sq, SyncQueueFrame frame) {
return (sq->type == SYNC_QUEUE_PACKETS) ? 0 : frame.f->nb_samples;
}
static int frame_null(const SyncQueue *sq, SyncQueueFrame frame)
{
return (sq->type == SYNC_QUEUE_PACKETS) ? (frame.p == NULL) : (frame.f == NULL);
static int frame_null(const SyncQueue *sq, SyncQueueFrame frame) {
return (sq->type == SYNC_QUEUE_PACKETS) ? (frame.p == NULL)
: (frame.f == NULL);
}
static void finish_stream(SyncQueue *sq, unsigned int stream_idx)
{
static void tb_update(const SyncQueue *sq, SyncQueueStream *st,
const SyncQueueFrame frame) {
AVRational tb = (sq->type == SYNC_QUEUE_PACKETS) ? frame.p->time_base
: frame.f->time_base;
av_assert0(tb.num > 0 && tb.den > 0);
if (tb.num == st->tb.num && tb.den == st->tb.den)
return;
// timebase should not change after the first frame
av_assert0(!av_fifo_can_read(st->fifo));
if (st->head_ts != AV_NOPTS_VALUE)
st->head_ts = av_rescale_q(st->head_ts, st->tb, tb);
st->tb = tb;
}
static void finish_stream(SyncQueue *sq, unsigned int stream_idx) {
SyncQueueStream *st = &sq->streams[stream_idx];
if (!st->finished)
av_log(sq->logctx, AV_LOG_DEBUG, "sq: finish %u; head ts %s\n",
stream_idx, av_ts2timestr(st->head_ts, &st->tb));
st->finished = 1;
if (st->limiting && st->head_ts != AV_NOPTS_VALUE) {
@ -113,14 +202,21 @@ static void finish_stream(SyncQueue *sq, unsigned int stream_idx)
sq->head_finished_stream = stream_idx;
}
/* mark as finished all streams that should no longer receive new frames,
* due to them being ahead of some finished stream */
/* mark as finished all streams that should no longer receive new
* frames, due to them being ahead of some finished stream */
st = &sq->streams[sq->head_finished_stream];
for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueStream *st1 = &sq->streams[i];
if (st != st1 && st1->head_ts != AV_NOPTS_VALUE &&
av_compare_ts(st->head_ts, st->tb, st1->head_ts, st1->tb) <= 0)
av_compare_ts(st->head_ts, st->tb, st1->head_ts, st1->tb) <=
0) {
if (!st1->finished)
av_log(sq->logctx, AV_LOG_DEBUG,
"sq: finish secondary %u; head ts %s\n", i,
av_ts2timestr(st1->head_ts, &st1->tb));
st1->finished = 1;
}
}
}
@ -130,36 +226,46 @@ static void finish_stream(SyncQueue *sq, unsigned int stream_idx)
return;
}
sq->finished = 1;
av_log(sq->logctx, AV_LOG_DEBUG, "sq: finish queue\n");
}
static void queue_head_update(SyncQueue *sq)
{
static void queue_head_update(SyncQueue *sq) {
av_assert0(sq->have_limiting);
if (sq->head_stream < 0) {
unsigned first_limiting = UINT_MAX;
/* wait for one timestamp in each stream before determining
* the queue head */
for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueStream *st = &sq->streams[i];
if (st->limiting && st->head_ts == AV_NOPTS_VALUE)
if (!st->limiting)
continue;
if (st->head_ts == AV_NOPTS_VALUE)
return;
if (first_limiting == UINT_MAX)
first_limiting = i;
}
// placeholder value, correct one will be found below
sq->head_stream = 0;
av_assert0(first_limiting < UINT_MAX);
sq->head_stream = first_limiting;
}
for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueStream *st_head = &sq->streams[sq->head_stream];
SyncQueueStream *st_head = &sq->streams[sq->head_stream];
SyncQueueStream *st_other = &sq->streams[i];
if (st_other->limiting && st_other->head_ts != AV_NOPTS_VALUE &&
av_compare_ts(st_other->head_ts, st_other->tb,
st_head->head_ts, st_head->tb) < 0)
av_compare_ts(st_other->head_ts, st_other->tb, st_head->head_ts,
st_head->tb) < 0)
sq->head_stream = i;
}
}
/* update this stream's head timestamp */
static void stream_update_ts(SyncQueue *sq, unsigned int stream_idx, int64_t ts)
{
static void stream_update_ts(SyncQueue *sq, unsigned int stream_idx,
int64_t ts) {
SyncQueueStream *st = &sq->streams[stream_idx];
if (ts == AV_NOPTS_VALUE ||
@ -172,13 +278,12 @@ static void stream_update_ts(SyncQueue *sq, unsigned int stream_idx, int64_t ts)
* this stream is also finished */
if (sq->head_finished_stream >= 0 &&
av_compare_ts(sq->streams[sq->head_finished_stream].head_ts,
sq->streams[sq->head_finished_stream].tb,
ts, st->tb) <= 0)
sq->streams[sq->head_finished_stream].tb, ts,
st->tb) <= 0)
finish_stream(sq, stream_idx);
/* update the overall head timestamp if it could have changed */
if (st->limiting &&
(sq->head_stream < 0 || sq->head_stream == stream_idx))
if (st->limiting && (sq->head_stream < 0 || sq->head_stream == stream_idx))
queue_head_update(sq);
}
@ -187,8 +292,7 @@ static void stream_update_ts(SyncQueue *sq, unsigned int stream_idx, int64_t ts)
*
* @return 1 if heartbeat triggered, 0 otherwise
*/
static int overflow_heartbeat(SyncQueue *sq, int stream_idx)
{
static int overflow_heartbeat(SyncQueue *sq, int stream_idx) {
SyncQueueStream *st;
SyncQueueFrame frame;
int64_t tail_ts = AV_NOPTS_VALUE;
@ -201,8 +305,8 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx)
st = &sq->streams[i];
if (st->head_ts != AV_NOPTS_VALUE &&
(ts == AV_NOPTS_VALUE ||
av_compare_ts(ts, sq->streams[stream_idx].tb,
st->head_ts, st->tb) < 0)) {
av_compare_ts(ts, sq->streams[stream_idx].tb, st->head_ts,
st->tb) < 0)) {
ts = st->head_ts;
stream_idx = i;
}
@ -215,16 +319,21 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx)
st = &sq->streams[stream_idx];
/* get the chosen stream's tail timestamp */
for (size_t i = 0; tail_ts == AV_NOPTS_VALUE &&
av_fifo_peek(st->fifo, &frame, 1, i) >= 0; i++)
tail_ts = frame_ts(sq, frame);
/* overflow triggers when the tail is over specified duration behind the head */
for (size_t i = 0;
tail_ts == AV_NOPTS_VALUE && av_fifo_peek(st->fifo, &frame, 1, i) >= 0;
i++)
tail_ts = frame_end(sq, frame, 0);
/* overflow triggers when the tail is over specified duration behind the
* head
*/
if (tail_ts == AV_NOPTS_VALUE || tail_ts >= st->head_ts ||
av_rescale_q(st->head_ts - tail_ts, st->tb, AV_TIME_BASE_Q) < sq->buf_size_us)
av_rescale_q(st->head_ts - tail_ts, st->tb, AV_TIME_BASE_Q) <
sq->buf_size_us)
return 0;
/* signal a fake timestamp for all streams that prevent tail_ts from being output */
/* signal a fake timestamp for all streams that prevent tail_ts from being
* output */
tail_ts++;
for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueStream *st1 = &sq->streams[i];
@ -239,72 +348,217 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx)
if (st1->head_ts != AV_NOPTS_VALUE)
ts = FFMAX(st1->head_ts + 1, ts);
av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u overflow heardbeat %s -> %s\n",
i, av_ts2timestr(st1->head_ts, &st1->tb),
av_ts2timestr(ts, &st1->tb));
stream_update_ts(sq, i, ts);
}
return 1;
}
int sq_send(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame)
{
int sq_send(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame) {
SyncQueueStream *st;
SyncQueueFrame dst;
int64_t ts;
int ret;
int ret, nb_samples;
av_assert0(stream_idx < sq->nb_streams);
st = &sq->streams[stream_idx];
av_assert0(st->tb.num > 0 && st->tb.den > 0);
if (frame_null(sq, frame)) {
av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u EOF\n", stream_idx);
finish_stream(sq, stream_idx);
return 0;
}
if (st->finished)
return AVERROR_EOF;
ret = objpool_get(sq->pool, (void**)&dst);
tb_update(sq, st, frame);
ret = objpool_get(sq->pool, (void **)&dst);
if (ret < 0)
return ret;
frame_move(sq, dst, frame);
ts = frame_ts(sq, dst);
nb_samples = frame_samples(sq, dst);
// make sure frame duration is consistent with sample count
if (nb_samples) {
av_assert0(dst.f->sample_rate > 0);
dst.f->duration = av_rescale_q(
nb_samples, (AVRational){1, dst.f->sample_rate}, dst.f->time_base);
}
ts = frame_end(sq, dst, 0);
av_log(sq->logctx, AV_LOG_DEBUG, "sq: send %u ts %s\n", stream_idx,
av_ts2timestr(ts, &st->tb));
ret = av_fifo_write(st->fifo, &dst, 1);
if (ret < 0) {
frame_move(sq, frame, dst);
objpool_release(sq->pool, (void**)&dst);
objpool_release(sq->pool, (void **)&dst);
return ret;
}
stream_update_ts(sq, stream_idx, ts);
st->frames_sent++;
if (st->frames_sent >= st->frames_max)
st->samples_queued += nb_samples;
st->samples_sent += nb_samples;
if (st->frame_samples)
st->frames_sent = st->samples_sent / st->frame_samples;
else
st->frames_sent++;
if (st->frames_sent >= st->frames_max) {
av_log(sq->logctx, AV_LOG_DEBUG,
"sq: %u frames_max %" PRIu64 " reached\n", stream_idx,
st->frames_max);
finish_stream(sq, stream_idx);
}
return 0;
}
static void offset_audio(AVFrame *f, int nb_samples) {
const int planar = av_sample_fmt_is_planar(f->format);
const int planes = planar ? f->ch_layout.nb_channels : 1;
const int bps = av_get_bytes_per_sample(f->format);
const int offset =
nb_samples * bps * (planar ? 1 : f->ch_layout.nb_channels);
av_assert0(bps > 0);
av_assert0(nb_samples < f->nb_samples);
for (int i = 0; i < planes; i++) {
f->extended_data[i] += offset;
if (i < FF_ARRAY_ELEMS(f->data))
f->data[i] = f->extended_data[i];
}
f->linesize[0] -= offset;
f->nb_samples -= nb_samples;
f->duration = av_rescale_q(f->nb_samples, (AVRational){1, f->sample_rate},
f->time_base);
f->pts +=
av_rescale_q(nb_samples, (AVRational){1, f->sample_rate}, f->time_base);
}
static int frame_is_aligned(const SyncQueue *sq, const AVFrame *frame) {
// only checks linesize[0], so only works for audio
av_assert0(frame->nb_samples > 0);
av_assert0(sq->align_mask);
// only check data[0], because we always offset all data pointers
// by the same offset, so if one is aligned, all are
if (!((uintptr_t)frame->data[0] & sq->align_mask) &&
!(frame->linesize[0] & sq->align_mask) &&
frame->linesize[0] > sq->align_mask)
return 1;
return 0;
}
static int receive_samples(SyncQueue *sq, SyncQueueStream *st, AVFrame *dst,
int nb_samples) {
SyncQueueFrame src;
int ret;
av_assert0(st->samples_queued >= nb_samples);
ret = av_fifo_peek(st->fifo, &src, 1, 0);
av_assert0(ret >= 0);
// peeked frame has enough samples and its data is aligned
// -> we can just make a reference and limit its sample count
if (src.f->nb_samples > nb_samples && frame_is_aligned(sq, src.f)) {
ret = av_frame_ref(dst, src.f);
if (ret < 0)
return ret;
dst->nb_samples = nb_samples;
offset_audio(src.f, nb_samples);
st->samples_queued -= nb_samples;
goto finish;
}
// otherwise allocate a new frame and copy the data
ret = av_channel_layout_copy(&dst->ch_layout, &src.f->ch_layout);
if (ret < 0)
return ret;
dst->format = src.f->format;
dst->nb_samples = nb_samples;
ret = av_frame_get_buffer(dst, 0);
if (ret < 0)
goto fail;
ret = av_frame_copy_props(dst, src.f);
if (ret < 0)
goto fail;
dst->nb_samples = 0;
while (dst->nb_samples < nb_samples) {
int to_copy;
ret = av_fifo_peek(st->fifo, &src, 1, 0);
av_assert0(ret >= 0);
to_copy = FFMIN(nb_samples - dst->nb_samples, src.f->nb_samples);
av_samples_copy(dst->extended_data, src.f->extended_data,
dst->nb_samples, 0, to_copy, dst->ch_layout.nb_channels,
dst->format);
if (to_copy < src.f->nb_samples)
offset_audio(src.f, to_copy);
else {
av_frame_unref(src.f);
objpool_release(sq->pool, (void **)&src);
av_fifo_drain2(st->fifo, 1);
}
st->samples_queued -= to_copy;
dst->nb_samples += to_copy;
}
finish:
dst->duration = av_rescale_q(nb_samples, (AVRational){1, dst->sample_rate},
dst->time_base);
return 0;
fail:
av_frame_unref(dst);
return ret;
}
static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx,
SyncQueueFrame frame)
{
SyncQueueStream *st_head = sq->head_stream >= 0 ?
&sq->streams[sq->head_stream] : NULL;
SyncQueueFrame frame) {
SyncQueueStream *st_head =
sq->head_stream >= 0 ? &sq->streams[sq->head_stream] : NULL;
SyncQueueStream *st;
av_assert0(stream_idx < sq->nb_streams);
st = &sq->streams[stream_idx];
if (av_fifo_can_read(st->fifo)) {
if (av_fifo_can_read(st->fifo) &&
(st->frame_samples <= st->samples_queued || st->finished)) {
int nb_samples = st->frame_samples;
SyncQueueFrame peek;
int64_t ts;
int cmp = 1;
if (st->finished)
nb_samples = FFMIN(nb_samples, st->samples_queued);
av_fifo_peek(st->fifo, &peek, 1, 0);
ts = frame_ts(sq, peek);
ts = frame_end(sq, peek, nb_samples);
/* check if this stream's tail timestamp does not overtake
* the overall queue head */
@ -313,21 +567,40 @@ static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx,
/* We can release frames that do not end after the queue head.
* Frames with no timestamps are just passed through with no conditions.
* Frames are also passed through when there are no limiting streams.
*/
if (cmp <= 0 || ts == AV_NOPTS_VALUE) {
frame_move(sq, frame, peek);
objpool_release(sq->pool, (void**)&peek);
av_fifo_drain2(st->fifo, 1);
if (cmp <= 0 || ts == AV_NOPTS_VALUE || !sq->have_limiting) {
if (nb_samples && (nb_samples != peek.f->nb_samples ||
!frame_is_aligned(sq, peek.f))) {
int ret = receive_samples(sq, st, frame.f, nb_samples);
if (ret < 0)
return ret;
} else {
frame_move(sq, frame, peek);
objpool_release(sq->pool, (void **)&peek);
av_fifo_drain2(st->fifo, 1);
av_assert0(st->samples_queued >= frame_samples(sq, frame));
st->samples_queued -= frame_samples(sq, frame);
}
av_log(sq->logctx, AV_LOG_DEBUG,
"sq: receive %u ts %s queue head %d ts %s\n", stream_idx,
av_ts2timestr(frame_end(sq, frame, 0), &st->tb),
sq->head_stream,
st_head ? av_ts2timestr(st_head->head_ts, &st_head->tb)
: "N/A");
return 0;
}
}
return (sq->finished || (st->finished && !av_fifo_can_read(st->fifo))) ?
AVERROR_EOF : AVERROR(EAGAIN);
return (sq->finished || (st->finished && !av_fifo_can_read(st->fifo)))
? AVERROR_EOF
: AVERROR(EAGAIN);
}
static int receive_internal(SyncQueue *sq, int stream_idx, SyncQueueFrame frame)
{
static int receive_internal(SyncQueue *sq, int stream_idx,
SyncQueueFrame frame) {
int nb_eof = 0;
int ret;
@ -350,8 +623,7 @@ static int receive_internal(SyncQueue *sq, int stream_idx, SyncQueueFrame frame)
return (nb_eof == sq->nb_streams) ? AVERROR_EOF : AVERROR(EAGAIN);
}
int sq_receive(SyncQueue *sq, int stream_idx, SyncQueueFrame frame)
{
int sq_receive(SyncQueue *sq, int stream_idx, SyncQueueFrame frame) {
int ret = receive_internal(sq, stream_idx, frame);
/* try again if the queue overflowed and triggered a fake heartbeat
@ -362,11 +634,11 @@ int sq_receive(SyncQueue *sq, int stream_idx, SyncQueueFrame frame)
return ret;
}
int sq_add_stream(SyncQueue *sq, int limiting)
{
int sq_add_stream(SyncQueue *sq, int limiting) {
SyncQueueStream *tmp, *st;
tmp = av_realloc_array(sq->streams, sq->nb_streams + 1, sizeof(*sq->streams));
tmp =
av_realloc_array(sq->streams, sq->nb_streams + 1, sizeof(*sq->streams));
if (!tmp)
return AVERROR(ENOMEM);
sq->streams = tmp;
@ -374,63 +646,64 @@ int sq_add_stream(SyncQueue *sq, int limiting)
st = &sq->streams[sq->nb_streams];
memset(st, 0, sizeof(*st));
st->fifo = av_fifo_alloc2(1, sizeof(SyncQueueFrame), AV_FIFO_FLAG_AUTO_GROW);
st->fifo =
av_fifo_alloc2(1, sizeof(SyncQueueFrame), AV_FIFO_FLAG_AUTO_GROW);
if (!st->fifo)
return AVERROR(ENOMEM);
/* we set a valid default, so that a pathological stream that never
* receives even a real timebase (and no frames) won't stall all other
* streams forever; cf. overflow_heartbeat() */
st->tb = (AVRational){ 1, 1 };
st->tb = (AVRational){1, 1};
st->head_ts = AV_NOPTS_VALUE;
st->frames_max = UINT64_MAX;
st->limiting = limiting;
st->limiting = limiting;
sq->have_limiting |= limiting;
return sq->nb_streams++;
}
void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb)
{
void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t frames) {
SyncQueueStream *st;
av_assert0(stream_idx < sq->nb_streams);
st = &sq->streams[stream_idx];
av_assert0(!av_fifo_can_read(st->fifo));
if (st->head_ts != AV_NOPTS_VALUE)
st->head_ts = av_rescale_q(st->head_ts, st->tb, tb);
st->tb = tb;
st->frames_max = frames;
if (st->frames_sent >= st->frames_max)
finish_stream(sq, stream_idx);
}
void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t frames)
{
void sq_frame_samples(SyncQueue *sq, unsigned int stream_idx,
int frame_samples) {
SyncQueueStream *st;
av_assert0(sq->type == SYNC_QUEUE_FRAMES);
av_assert0(stream_idx < sq->nb_streams);
st = &sq->streams[stream_idx];
st->frames_max = frames;
if (st->frames_sent >= st->frames_max)
finish_stream(sq, stream_idx);
st->frame_samples = frame_samples;
sq->align_mask = av_cpu_max_align() - 1;
}
SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us)
{
SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us,
void *logctx) {
SyncQueue *sq = av_mallocz(sizeof(*sq));
if (!sq)
return NULL;
sq->type = type;
sq->buf_size_us = buf_size_us;
sq->type = type;
sq->buf_size_us = buf_size_us;
sq->logctx = logctx;
sq->head_stream = -1;
sq->head_stream = -1;
sq->head_finished_stream = -1;
sq->pool = (type == SYNC_QUEUE_PACKETS) ? objpool_alloc_packets() :
objpool_alloc_frames();
sq->pool = (type == SYNC_QUEUE_PACKETS) ? objpool_alloc_packets()
: objpool_alloc_frames();
if (!sq->pool) {
av_freep(&sq);
return NULL;
@ -439,8 +712,7 @@ SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us)
return sq;
}
void sq_free(SyncQueue **psq)
{
void sq_free(SyncQueue **psq) {
SyncQueue *sq = *psq;
if (!sq)
@ -449,7 +721,7 @@ void sq_free(SyncQueue **psq)
for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueFrame frame;
while (av_fifo_read(sq->streams[i].fifo, &frame, 1) >= 0)
objpool_release(sq->pool, (void**)&frame);
objpool_release(sq->pool, (void **)&frame);
av_fifo_freep2(&sq->streams[i].fifo);
}

@ -1,6 +1,6 @@
/*
* This file is part of FFmpeg.
* Copyright (c) 2023 ARTHENICA LTD
* Copyright (c) 2023-2024 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,12 +18,17 @@
*/
/*
* This file is the modified version of sync_queue.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of sync_queue.h file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
@ -44,13 +49,18 @@ enum SyncQueueType {
};
typedef union SyncQueueFrame {
AVFrame *f;
AVFrame *f;
AVPacket *p;
} SyncQueueFrame;
#define SQFRAME(frame) ((SyncQueueFrame){ .f = (frame) })
#define SQPKT(pkt) ((SyncQueueFrame){ .p = (pkt) })
#define SQFRAME(frame) ((SyncQueueFrame){.f = (frame)})
#define SQPKT(pkt) ((SyncQueueFrame){.p = (pkt)})
/**
* A sync queue provides timestamp synchronization between multiple streams.
* Some of these streams are marked as "limiting", then the queue ensures no
* stream gets ahead of any of the limiting streams.
*/
typedef struct SyncQueue SyncQueue;
/**
@ -58,8 +68,8 @@ typedef struct SyncQueue SyncQueue;
*
* @param buf_size_us maximum duration that will be buffered in microseconds
*/
SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us);
void sq_free(SyncQueue **sq);
SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us, void *logctx);
void sq_free(SyncQueue **sq);
/**
* Add a new stream to the sync queue.
@ -72,12 +82,6 @@ void sq_free(SyncQueue **sq);
*/
int sq_add_stream(SyncQueue *sq, int limiting);
/**
* Set the timebase for the stream with index stream_idx. Should be called
* before sending any frames for this stream.
*/
void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb);
/**
* Limit the number of output frames for stream with index stream_idx
* to max_frames.
@ -85,6 +89,16 @@ void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb);
void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx,
uint64_t max_frames);
/**
* Set a constant output audio frame size, in samples. Can only be used with
* SYNC_QUEUE_FRAMES queues and audio streams.
*
* All output frames will have exactly frame_samples audio samples, except
* possibly for the last one, which may have fewer.
*/
void sq_frame_samples(SyncQueue *sq, unsigned int stream_idx,
int frame_samples);
/**
* Submit a frame for the stream with index stream_idx.
*

@ -18,8 +18,9 @@
*/
/*
* This file is the modified version of thread_queue.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of thread_queue.c file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
@ -49,25 +50,24 @@ enum {
};
typedef struct FifoElem {
void *obj;
void *obj;
unsigned int stream_idx;
} FifoElem;
struct ThreadQueue {
int *finished;
unsigned int nb_streams;
int *finished;
unsigned int nb_streams;
AVFifo *fifo;
AVFifo *fifo;
ObjPool *obj_pool;
void (*obj_move)(void *dst, void *src);
void (*obj_move)(void *dst, void *src);
pthread_mutex_t lock;
pthread_cond_t cond;
pthread_cond_t cond;
};
void tq_free(ThreadQueue **ptq)
{
void tq_free(ThreadQueue **ptq) {
ThreadQueue *tq = *ptq;
if (!tq)
@ -91,8 +91,8 @@ void tq_free(ThreadQueue **ptq)
}
ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size,
ObjPool *obj_pool, void (*obj_move)(void *dst, void *src))
{
ObjPool *obj_pool,
void (*obj_move)(void *dst, void *src)) {
ThreadQueue *tq;
int ret;
@ -131,8 +131,7 @@ fail:
return NULL;
}
int tq_send(ThreadQueue *tq, unsigned int stream_idx, void *data)
{
int tq_send(ThreadQueue *tq, unsigned int stream_idx, void *data) {
int *finished;
int ret;
@ -153,7 +152,7 @@ int tq_send(ThreadQueue *tq, unsigned int stream_idx, void *data)
ret = AVERROR_EOF;
*finished |= FINISHED_SEND;
} else {
FifoElem elem = { .stream_idx = stream_idx };
FifoElem elem = {.stream_idx = stream_idx};
ret = objpool_get(tq->obj_pool, &elem.obj);
if (ret < 0)
@ -172,9 +171,7 @@ finish:
return ret;
}
static int receive_locked(ThreadQueue *tq, int *stream_idx,
void *data)
{
static int receive_locked(ThreadQueue *tq, int *stream_idx, void *data) {
FifoElem elem;
unsigned int nb_finished = 0;
@ -192,7 +189,7 @@ static int receive_locked(ThreadQueue *tq, int *stream_idx,
/* return EOF to the consumer at most once for each stream */
if (!(tq->finished[i] & FINISHED_RECV)) {
tq->finished[i] |= FINISHED_RECV;
*stream_idx = i;
*stream_idx = i;
return AVERROR_EOF;
}
@ -202,8 +199,7 @@ static int receive_locked(ThreadQueue *tq, int *stream_idx,
return nb_finished == tq->nb_streams ? AVERROR_EOF : AVERROR(EAGAIN);
}
int tq_receive(ThreadQueue *tq, int *stream_idx, void *data)
{
int tq_receive(ThreadQueue *tq, int *stream_idx, void *data) {
int ret;
*stream_idx = -1;
@ -228,8 +224,7 @@ int tq_receive(ThreadQueue *tq, int *stream_idx, void *data)
return ret;
}
void tq_send_finish(ThreadQueue *tq, unsigned int stream_idx)
{
void tq_send_finish(ThreadQueue *tq, unsigned int stream_idx) {
av_assert0(stream_idx < tq->nb_streams);
pthread_mutex_lock(&tq->lock);
@ -243,8 +238,7 @@ void tq_send_finish(ThreadQueue *tq, unsigned int stream_idx)
pthread_mutex_unlock(&tq->lock);
}
void tq_receive_finish(ThreadQueue *tq, unsigned int stream_idx)
{
void tq_receive_finish(ThreadQueue *tq, unsigned int stream_idx) {
av_assert0(stream_idx < tq->nb_streams);
pthread_mutex_lock(&tq->lock);

@ -18,8 +18,9 @@
*/
/*
* This file is the modified version of thread_queue.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of thread_queue.h file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
@ -50,8 +51,9 @@ typedef struct ThreadQueue ThreadQueue;
* @param callback that moves the contents between two data pointers
*/
ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size,
ObjPool *obj_pool, void (*obj_move)(void *dst, void *src));
void tq_free(ThreadQueue **tq);
ObjPool *obj_pool,
void (*obj_move)(void *dst, void *src));
void tq_free(ThreadQueue **tq);
/**
* Send an item for the given stream to the queue.

@ -101,4 +101,11 @@ public class AbiDetect {
*/
native static String getNativeBuildConf();
/**
* <p>Returns the minimum Android API level required to run this library.
*
* @return minimum Android API level required to run this library
*/
public native static String getNativeMinSdk();
}

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2022 Taner Sener
* Copyright (c) 2024 ARTHENICA LTD
*
* This file is part of FFmpegKit.
*
@ -34,6 +35,8 @@ import com.arthenica.smartexception.java.Exceptions;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -46,6 +49,7 @@ import java.util.StringTokenizer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@ -113,7 +117,7 @@ public class FFmpegKitConfig {
/* Session history variables */
private static int sessionHistorySize;
private static final Map<Long, Session> sessionHistoryMap;
private static final List<Session> sessionHistoryList;
private static final ArrayList<Session> sessionHistoryList;
private static final Object sessionHistoryLock;
private static int asyncConcurrencyLimit;
@ -128,6 +132,7 @@ public class FFmpegKitConfig {
private static final SparseArray<SAFProtocolUrl> safIdMap;
private static final SparseArray<SAFProtocolUrl> safFileDescriptorMap;
private static LogRedirectionStrategy globalLogRedirectionStrategy;
private static final AtomicBoolean safUrlsReusable;
static {
@ -160,7 +165,7 @@ public class FFmpegKitConfig {
return (this.size() > sessionHistorySize);
}
};
sessionHistoryList = new LinkedList<>();
sessionHistoryList = new ArrayList<>();
sessionHistoryLock = new Object();
globalLogCallback = null;
@ -172,8 +177,9 @@ public class FFmpegKitConfig {
safIdMap = new SparseArray<>();
safFileDescriptorMap = new SparseArray<>();
globalLogRedirectionStrategy = LogRedirectionStrategy.PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED;
safUrlsReusable = new AtomicBoolean(false);
android.util.Log.i(FFmpegKitConfig.TAG, String.format("Loaded ffmpeg-kit-%s-%s-%s-%s.", NativeLoader.loadPackageName(), NativeLoader.loadAbi(), NativeLoader.loadVersion(), NativeLoader.loadBuildDate()));
android.util.Log.i(FFmpegKitConfig.TAG, String.format("Loaded ffmpeg-kit-%s-%s-%s-api%s-%s.", NativeLoader.loadPackageName(), NativeLoader.loadAbi(), NativeLoader.loadVersion(), NativeLoader.loadMinSdk(), NativeLoader.loadBuildDate()));
}
/**
@ -487,7 +493,8 @@ public class FFmpegKitConfig {
/**
* <p>Creates a new named pipe to use in <code>FFmpeg</code> operations.
*
* <p>Please note that creator is responsible of closing created pipes.
* <p>Please note that creator is responsible of closing created pipes via the
* {@link #closeFFmpegPipe} method.
*
* @param context application context
* @return the full path of the named pipe
@ -567,18 +574,17 @@ public class FFmpegKitConfig {
* @return FFmpegKit version
*/
public static String getVersion() {
if (isLTSBuild()) {
return String.format("%s-lts", getNativeVersion());
} else {
return getNativeVersion();
}
return getNativeVersion();
}
/**
* <p>Returns whether FFmpegKit release is a Long Term Release or not.
*
* @return true/yes or false/no
* @deprecated as of version 6.1.2, use the {@link AbiDetect#getNativeMinSdk()} method to
* determine the features supported by this version
*/
@Deprecated(since = "6.1.2", forRemoval = true)
public static boolean isLTSBuild() {
return AbiDetect.isNativeLTSBuild();
}
@ -1029,6 +1035,7 @@ public class FFmpegKitConfig {
safUrl.setParcelFileDescriptor(parcelFileDescriptor);
final int fd = parcelFileDescriptor.getFd();
safFileDescriptorMap.put(fd, safUrl);
android.util.Log.d(TAG, String.format("Generated fd %d for SAF id %d.", fd, safId));
return fd;
} else {
android.util.Log.e(TAG, String.format("SAF id %d not found.", safId));
@ -1050,11 +1057,15 @@ public class FFmpegKitConfig {
try {
final SAFProtocolUrl safProtocolUrl = safFileDescriptorMap.get(fileDescriptor);
if (safProtocolUrl != null) {
final int safId = safProtocolUrl.getSafId();
ParcelFileDescriptor parcelFileDescriptor = safProtocolUrl.getParcelFileDescriptor();
if (parcelFileDescriptor != null) {
safFileDescriptorMap.delete(fileDescriptor);
safIdMap.delete(safProtocolUrl.getSafId());
parcelFileDescriptor.close();
if (!safUrlsReusable.get()) {
safIdMap.delete(safId);
}
android.util.Log.d(TAG, String.format("Closed fd %d for SAF id %d.", fileDescriptor, safId));
return 1;
} else {
android.util.Log.e(TAG, String.format("ParcelFileDescriptor for SAF fd %d not found.", fileDescriptor));
@ -1069,6 +1080,31 @@ public class FFmpegKitConfig {
return 0;
}
/**
* Unregisters saf protocol urls and cleans up the resources associated with them.
*
* @param safUrl saf protocol url e.g. saf:1.mp4
*/
public static void unregisterSafProtocolUrl(final String safUrl) {
if (safUrl != null) {
try {
URI uri = new URI(safUrl);
String path = uri.getSchemeSpecificPart();
int index = path.indexOf(".");
if (index > -1) {
String safIdString = path.substring(0, index);
int safId = Integer.parseInt(safIdString);
safIdMap.delete(safId);
android.util.Log.d(TAG, String.format("Unregistered safUrl %s successfully.", safUrl));
} else {
android.util.Log.w(FFmpegKitConfig.TAG, String.format("Cannot unregister safUrl %s. Failed to drop extension!", safUrl));
}
} catch (URISyntaxException | NumberFormatException e) {
android.util.Log.w(FFmpegKitConfig.TAG, String.format("Cannot unregister safUrl %s. Failed to extract saf id!", safUrl));
}
}
}
/**
* Returns the session history size.
*
@ -1144,6 +1180,20 @@ public class FFmpegKitConfig {
}
}
/**
* Deletes the session specified with <code>sessionId</code> from the session history.
*
* @param sessionId session identifier
*/
public static void deleteSession(final long sessionId) {
synchronized (sessionHistoryLock) {
Session removedSession = sessionHistoryMap.remove(sessionId);
if (removedSession != null) {
sessionHistoryList.remove(removedSession);
}
}
}
/**
* Returns the last session created from the session history.
*
@ -1295,6 +1345,31 @@ public class FFmpegKitConfig {
FFmpegKitConfig.globalLogRedirectionStrategy = logRedirectionStrategy;
}
/**
* Returns if SAF protocol urls are reusable or not.
*
* @return true if SAF protocol urls are reusable, false otherwise
*/
public static boolean getSafUrlsReusable() {
return safUrlsReusable.get();
}
/**
* Defines whether the generated SAF protocol urls will be reusable or not.
*
* <p>Note that SAF protocol urls are not reusable by default and are automatically
* unregistered when the file associated with them is closed.
*
* <p>If they are set to be reused using this method, automatic unregistration will be
* disabled. Therefore, it will be the developer's responsibility to unregister them via the
* {@link #unregisterSafProtocolUrl} method.
*
* @param safUrlsReusable set to true to enable the reuse of SAF protocol urls
*/
public static void setSafUrlsReusable(final boolean safUrlsReusable) {
FFmpegKitConfig.safUrlsReusable.compareAndSet(!safUrlsReusable, safUrlsReusable);
}
/**
* Converts session state to string.
*
@ -1423,6 +1498,13 @@ public class FFmpegKitConfig {
*/
private native static String getNativeVersion();
/**
* <p>Returns the native FFmpegKit package name.
*
* @return native FFmpegKit package name
*/
native static String getNativePackageName();
/**
* <p>Synchronously executes FFmpeg natively.
*

@ -76,6 +76,14 @@ public class NativeLoader {
}
}
static String loadMinSdk() {
if (isTestModeDisabled()) {
return AbiDetect.getNativeMinSdk();
} else {
return String.format(Locale.getDefault(), "%d", Build.VERSION.SDK_INT);
}
}
static String loadPackageName() {
if (isTestModeDisabled()) {
return Packages.getPackageName();
@ -85,25 +93,15 @@ public class NativeLoader {
}
static String loadVersion() {
final String version = "6.0";
final String version = "6.1.2";
if (isTestModeDisabled()) {
return FFmpegKitConfig.getVersion();
} else if (loadIsLTSBuild()) {
return String.format("%s-lts", version);
} else {
return version;
}
}
static boolean loadIsLTSBuild() {
if (isTestModeDisabled()) {
return AbiDetect.isNativeLTSBuild();
} else {
return true;
}
}
static int loadLogLevel() {
if (isTestModeDisabled()) {
return FFmpegKitConfig.getNativeLogLevel();

@ -74,6 +74,10 @@ public class Packages {
* @return predicted FFmpegKit binary package name
*/
public static String getPackageName() {
String nativePackageName = FFmpegKitConfig.getNativePackageName();
if (nativePackageName != null && !nativePackageName.isEmpty()) {
return nativePackageName;
}
final List<String> externalLibraryList = getExternalLibraries();
final boolean speex = externalLibraryList.contains("speex");
final boolean fribidi = externalLibraryList.contains("fribidi");

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2021 Taner Sener
* Copyright (c) 2024 ARTHENICA LTD
*
* This file is part of FFmpegKit.
*
@ -187,6 +188,11 @@ public class FFmpegKitConfigTest {
}
}
@Test
public void unregisterSafProtocolUrl() {
FFmpegKitConfig.unregisterSafProtocolUrl("saf:1.mp4");
}
private String listToPackageName(final List<String> externalLibraryList) {
boolean speex = externalLibraryList.contains("speex");
boolean fribidi = externalLibraryList.contains("fribidi");

Binary file not shown.

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

281
android/gradlew vendored

@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -17,67 +17,98 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -98,88 +129,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
i=`expr $i + 1`
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

@ -1,59 +1,50 @@
MY_LOCAL_PATH := $(call my-dir)
$(call import-add-path, $(MY_LOCAL_PATH))
include $(MY_LOCAL_PATH)/build.mk
MY_ARMV7 := false
MY_ARMV7_NEON := false
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
ifeq ("$(shell test -e $(MY_LOCAL_PATH)/../build/.armv7 && echo armv7)","armv7")
MY_ARMV7 := true
endif
ifeq ("$(shell test -e $(MY_LOCAL_PATH)/../build/.armv7neon && echo armv7neon)","armv7neon")
MY_ARMV7_NEON := true
endif
MY_ARMV7 := ${ARMV7}
MY_ARMV7_NEON := ${ARMV7_NEON}
endif
ifeq ("$(shell test -e $(MY_LOCAL_PATH)/../build/.lts && echo lts)","lts")
MY_LTS_POSTFIX := -lts
else
MY_LTS_POSTFIX :=
endif
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
ifeq ($(MY_ARMV7_NEON), true)
MY_BUILD_DIR := android-$(TARGET_ARCH)-neon$(MY_LTS_POSTFIX)
else
MY_BUILD_DIR := android-$(TARGET_ARCH)$(MY_LTS_POSTFIX)
endif
else
MY_BUILD_DIR := android-$(TARGET_ARCH)$(MY_LTS_POSTFIX)
endif
FFMPEG_INCLUDES := $(MY_LOCAL_PATH)/../../prebuilt/$(MY_BUILD_DIR)/ffmpeg/include
# DEFINE ARCH FLAGS
MY_ARM_MODE := arm
MY_ARM_NEON := false
LOCAL_PATH := $(MY_LOCAL_PATH)/../ffmpeg-kit-android-lib/src/main/cpp
# DEFINE ARCH FLAGS
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
MY_ARCH_FLAGS := ARM_V7A
ifeq ("$(shell test -e $(MY_LOCAL_PATH)/../build/.lts && echo lts)","lts")
MY_ARM_NEON := false
MY_ARM_NEON := true
ifeq ($(MY_ARMV7_NEON), true)
MY_BUILD_DIR := $(ARMV7_NEON_BUILD_PATH)
else
MY_ARM_NEON := true
MY_BUILD_DIR := $(ARMV7_BUILD_PATH)
endif
endif
ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
MY_ARCH_FLAGS := ARM64_V8A
MY_ARM_NEON := true
MY_BUILD_DIR := $(ARM64_BUILD_PATH)
endif
ifeq ($(TARGET_ARCH_ABI), x86)
MY_ARCH_FLAGS := X86
MY_ARM_NEON := true
MY_BUILD_DIR := $(X86_BUILD_PATH)
endif
ifeq ($(TARGET_ARCH_ABI), x86_64)
MY_ARCH_FLAGS := X86_64
MY_ARM_NEON := true
MY_BUILD_DIR := $(X86_64_BUILD_PATH)
endif
FFMPEG_INCLUDES := $(MY_LOCAL_PATH)/../../prebuilt/$(MY_BUILD_DIR)/ffmpeg/include
LOCAL_PATH := $(MY_LOCAL_PATH)/../ffmpeg-kit-android-lib/src/main/cpp
# 16 kb page size support for arm64-v8a and x86_64
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
MY_LDFLAGS := -Wl,-z,max-page-size=16384
else ifeq ($(TARGET_ARCH_ABI),x86_64)
MY_LDFLAGS := -Wl,-z,max-page-size=16384
endif
include $(CLEAR_VARS)
@ -62,6 +53,7 @@ LOCAL_MODULE := ffmpegkit_abidetect
LOCAL_SRC_FILES := ffmpegkit_abidetect.c
LOCAL_CFLAGS := -Wall -Wextra -Werror -Wno-unused-parameter -DFFMPEG_KIT_${MY_ARCH_FLAGS}
LOCAL_C_INCLUDES := $(FFMPEG_INCLUDES)
LOCAL_LDFLAGS := $(MY_LDFLAGS)
LOCAL_LDLIBS := -llog -lz -landroid
LOCAL_STATIC_LIBRARIES := cpu-features
LOCAL_ARM_NEON := ${MY_ARM_NEON}
@ -69,13 +61,7 @@ include $(BUILD_SHARED_LIBRARY)
$(call import-module, cpu-features)
MY_SRC_FILES := ffmpegkit.c ffprobekit.c ffmpegkit_exception.c fftools_cmdutils.c fftools_ffmpeg.c fftools_ffprobe.c fftools_ffmpeg_mux.c fftools_ffmpeg_mux_init.c fftools_ffmpeg_demux.c fftools_ffmpeg_opt.c fftools_opt_common.c fftools_ffmpeg_hw.c fftools_ffmpeg_filter.c fftools_objpool.c fftools_sync_queue.c fftools_thread_queue.c
ifeq ($(TARGET_PLATFORM),android-16)
MY_SRC_FILES += android_lts_support.c
else ifeq ($(TARGET_PLATFORM),android-17)
MY_SRC_FILES += android_lts_support.c
endif
MY_SRC_FILES := ffmpegkit.c ffprobekit.c ffmpegkit_exception.c fftools_cmdutils.c fftools_ffmpeg.c fftools_ffprobe.c fftools_ffmpeg_mux.c fftools_ffmpeg_mux_init.c fftools_ffmpeg_demux.c fftools_ffmpeg_enc.c fftools_ffmpeg_dec.c fftools_ffmpeg_opt.c fftools_opt_common.c fftools_ffmpeg_hw.c fftools_ffmpeg_filter.c fftools_objpool.c fftools_sync_queue.c fftools_thread_queue.c android_support.c ffmpeg_context.c
MY_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare
MY_LDLIBS := -llog -lz -landroid
@ -89,6 +75,7 @@ ifeq ($(MY_ARMV7_NEON), true)
LOCAL_MODULE := ffmpegkit_armv7a_neon
LOCAL_SRC_FILES := $(MY_SRC_FILES)
LOCAL_CFLAGS := $(MY_CFLAGS)
LOCAL_LDFLAGS := $(MY_LDFLAGS)
LOCAL_LDLIBS := $(MY_LDLIBS)
LOCAL_SHARED_LIBRARIES := libavcodec_neon libavfilter_neon libswscale_neon libavformat_neon libavutil_neon libswresample_neon libavdevice_neon
ifeq ($(APP_STL), c++_shared)
@ -111,6 +98,7 @@ ifeq ($(MY_BUILD_GENERIC_FFMPEG_KIT), true)
LOCAL_MODULE := ffmpegkit
LOCAL_SRC_FILES := $(MY_SRC_FILES)
LOCAL_CFLAGS := $(MY_CFLAGS)
LOCAL_LDFLAGS := $(MY_LDFLAGS)
LOCAL_LDLIBS := $(MY_LDLIBS)
LOCAL_SHARED_LIBRARIES := libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
ifeq ($(APP_STL), c++_shared)

@ -1,8 +1,16 @@
ifeq ($(MY_LTS_POSTFIX),-lts)
LOCAL_PATH := $(call my-dir)/../../../prebuilt/android-$(TARGET_ARCH)-lts/ffmpeg/lib
else
LOCAL_PATH := $(call my-dir)/../../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/lib
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
MY_BUILD_DIR := $(ARMV7_BUILD_PATH)
endif
ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
MY_BUILD_DIR := $(ARM64_BUILD_PATH)
endif
ifeq ($(TARGET_ARCH_ABI), x86)
MY_BUILD_DIR := $(X86_BUILD_PATH)
endif
ifeq ($(TARGET_ARCH_ABI), x86_64)
MY_BUILD_DIR := $(X86_64_BUILD_PATH)
endif
LOCAL_PATH := $(call my-dir)/../../../prebuilt/$(MY_BUILD_DIR)/ffmpeg/lib
MY_ARM_MODE := arm

@ -1,8 +1,4 @@
ifeq ($(MY_LTS_POSTFIX),-lts)
LOCAL_PATH := $(call my-dir)/../../../../prebuilt/android-$(TARGET_ARCH)-neon-lts/ffmpeg/lib
else
LOCAL_PATH := $(call my-dir)/../../../../prebuilt/android-$(TARGET_ARCH)-neon/ffmpeg/lib
endif
LOCAL_PATH := $(call my-dir)/../../../../prebuilt/$(MY_BUILD_DIR)/ffmpeg/lib
MY_ARM_MODE := arm
MY_ARM_NEON := true

@ -25,8 +25,7 @@ After that this script should be used to create an umbrella xcframework.\n"
echo -e "Options:"
echo -e " -h, --help\t\t\tdisplay this help and exit"
echo -e " -v, --version\t\t\tdisplay version information and exit"
echo -e " -f, --force\t\t\tignore warnings"
echo -e " -l, --lts\t\t\tinclude lts packages to support iOS 10+, tvOS 10+, macOS 10.12+ devices\n"
echo -e " -f, --force\t\t\tignore warnings\n"
echo -e "Architectures:"
echo -e " --disable-iphoneos\t\tdo not include iphoneos architecture variant [yes]"
@ -124,7 +123,7 @@ source "${BASEDIR}"/scripts/variable.sh
export FFMPEG_KIT_BUILD_TYPE="apple"
source "${BASEDIR}"/scripts/function-${FFMPEG_KIT_BUILD_TYPE}.sh
# SET DEFAULTS SETTINGS
# SET DEFAULT SETTINGS
enable_default_architecture_variants
# SELECT XCODE VERSION USED FOR BUILDING
@ -137,7 +136,8 @@ fi
DETECTED_IOS_SDK_VERSION="$(xcrun --sdk iphoneos --show-sdk-version 2>>"${BASEDIR}"/build.log)"
DETECTED_TVOS_SDK_VERSION="$(xcrun --sdk appletvos --show-sdk-version 2>>"${BASEDIR}"/build.log)"
DETECTED_MACOS_SDK_VERSION="$(xcrun --sdk macosx --show-sdk-version 2>>"${BASEDIR}"/build.log)"
echo -e "INFO: Using iOS SDK: ${DETECTED_IOS_SDK_VERSION}, tvOS SDK: ${DETECTED_TVOS_SDK_VERSION}, macOS SDK: ${DETECTED_MACOS_SDK_VERSION} by Xcode provided at $(xcode-select -p)\n" 1>>"${BASEDIR}"/build.log 2>&1
XCODE_PATH=$(xcode-select -p 2>>"${BASEDIR}"/build.log)
echo -e "INFO: Using iOS SDK: ${DETECTED_IOS_SDK_VERSION}, tvOS SDK: ${DETECTED_TVOS_SDK_VERSION}, macOS SDK: ${DETECTED_MACOS_SDK_VERSION} by Xcode provided at ${XCODE_PATH}\n" 1>>"${BASEDIR}"/build.log 2>&1
echo -e "INFO: Build options: $*\n" 1>>"${BASEDIR}"/build.log 2>&1
# SET DEFAULT BUILD OPTIONS
@ -152,14 +152,6 @@ if [[ -z ${BUILD_VERSION} ]]; then
exit 1
fi
# PROCESS LTS BUILD OPTION FIRST AND SET BUILD TYPE: MAIN OR LTS
for argument in "$@"; do
if [[ "$argument" == "-l" ]] || [[ "$argument" == "--lts" ]]; then
export FFMPEG_KIT_LTS_BUILD="1"
BUILD_TYPE_ID+="LTS "
fi
done
# PROCESS BUILD OPTIONS
while [ ! $# -eq 0 ]; do
case $1 in
@ -170,7 +162,6 @@ while [ ! $# -eq 0 ]; do
display_version
exit 0
;;
-l | --lts) ;;
-f | --force)
export BUILD_FORCE="1"
;;

10
apple/.gitignore vendored

@ -1,5 +1,6 @@
/autom4te.cache/
/Makefile
/Makefile.in
/config.status
/configure.tmp
/configure~
@ -14,4 +15,11 @@
/configure
/libtool
/ltmain.sh
/Makefile.in
/aclocal.m4
/ar-lib
/compile
/config.guess
/config.sub
/depcomp
/install-sh
/missing

@ -38,7 +38,7 @@ PROJECT_NAME = "FFmpegKit iOS / macOS / tvOS API"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 6.0
PROJECT_NUMBER = 6.1.2
# 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

1251
apple/aclocal.m4 vendored

File diff suppressed because it is too large Load Diff

@ -1,271 +0,0 @@
#! /bin/sh
# Wrapper for Microsoft lib.exe
me=ar-lib
scriptversion=2019-07-04.01; # UTC
# Copyright (C) 2010-2021 Free Software Foundation, Inc.
# Written by Peter Rosin <peda@lysator.liu.se>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
# func_error message
func_error ()
{
echo "$me: $1" 1>&2
exit 1
}
file_conv=
# func_file_conv build_file
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN* | MSYS*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv in
mingw)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin | msys)
file=`cygpath -m "$file" || echo "$file"`
;;
wine)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_at_file at_file operation archive
# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE
# for each of them.
# When interpreting the content of the @FILE, do NOT use func_file_conv,
# since the user would need to supply preconverted file names to
# binutils ar, at least for MinGW.
func_at_file ()
{
operation=$2
archive=$3
at_file_contents=`cat "$1"`
eval set x "$at_file_contents"
shift
for member
do
$AR -NOLOGO $operation:"$member" "$archive" || exit $?
done
}
case $1 in
'')
func_error "no command. Try '$0 --help' for more information."
;;
-h | --h*)
cat <<EOF
Usage: $me [--help] [--version] PROGRAM ACTION ARCHIVE [MEMBER...]
Members may be specified in a file named with @FILE.
EOF
exit $?
;;
-v | --v*)
echo "$me, version $scriptversion"
exit $?
;;
esac
if test $# -lt 3; then
func_error "you must specify a program, an action and an archive"
fi
AR=$1
shift
while :
do
if test $# -lt 2; then
func_error "you must specify a program, an action and an archive"
fi
case $1 in
-lib | -LIB \
| -ltcg | -LTCG \
| -machine* | -MACHINE* \
| -subsystem* | -SUBSYSTEM* \
| -verbose | -VERBOSE \
| -wx* | -WX* )
AR="$AR $1"
shift
;;
*)
action=$1
shift
break
;;
esac
done
orig_archive=$1
shift
func_file_conv "$orig_archive"
archive=$file
# strip leading dash in $action
action=${action#-}
delete=
extract=
list=
quick=
replace=
index=
create=
while test -n "$action"
do
case $action in
d*) delete=yes ;;
x*) extract=yes ;;
t*) list=yes ;;
q*) quick=yes ;;
r*) replace=yes ;;
s*) index=yes ;;
S*) ;; # the index is always updated implicitly
c*) create=yes ;;
u*) ;; # TODO: don't ignore the update modifier
v*) ;; # TODO: don't ignore the verbose modifier
*)
func_error "unknown action specified"
;;
esac
action=${action#?}
done
case $delete$extract$list$quick$replace,$index in
yes,* | ,yes)
;;
yesyes*)
func_error "more than one action specified"
;;
*)
func_error "no action specified"
;;
esac
if test -n "$delete"; then
if test ! -f "$orig_archive"; then
func_error "archive not found"
fi
for member
do
case $1 in
@*)
func_at_file "${1#@}" -REMOVE "$archive"
;;
*)
func_file_conv "$1"
$AR -NOLOGO -REMOVE:"$file" "$archive" || exit $?
;;
esac
done
elif test -n "$extract"; then
if test ! -f "$orig_archive"; then
func_error "archive not found"
fi
if test $# -gt 0; then
for member
do
case $1 in
@*)
func_at_file "${1#@}" -EXTRACT "$archive"
;;
*)
func_file_conv "$1"
$AR -NOLOGO -EXTRACT:"$file" "$archive" || exit $?
;;
esac
done
else
$AR -NOLOGO -LIST "$archive" | tr -d '\r' | sed -e 's/\\/\\\\/g' \
| while read member
do
$AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $?
done
fi
elif test -n "$quick$replace"; then
if test ! -f "$orig_archive"; then
if test -z "$create"; then
echo "$me: creating $orig_archive"
fi
orig_archive=
else
orig_archive=$archive
fi
for member
do
case $1 in
@*)
func_file_conv "${1#@}"
set x "$@" "@$file"
;;
*)
func_file_conv "$1"
set x "$@" "$file"
;;
esac
shift
shift
done
if test -n "$orig_archive"; then
$AR -NOLOGO -OUT:"$archive" "$orig_archive" "$@" || exit $?
else
$AR -NOLOGO -OUT:"$archive" "$@" || exit $?
fi
elif test -n "$list"; then
if test ! -f "$orig_archive"; then
func_error "archive not found"
fi
$AR -NOLOGO -LIST "$archive" || exit $?
fi

@ -1,348 +0,0 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2021 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN* | MSYS*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/* | msys/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

1748
apple/config.guess vendored

File diff suppressed because it is too large Load Diff

1884
apple/config.sub vendored

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
# ffmpeg-kit 6.0 configure.ac
# ffmpeg-kit 6.1.2 configure.ac
AC_INIT([ffmpeg-kit], [6.0], [https://github.com/arthenica/ffmpeg-kit/issues/new])
AC_INIT([ffmpeg-kit], [6.1.2], [https://github.com/arthenica/ffmpeg-kit/issues/new])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([src/FFmpegKit.m])

@ -1,791 +0,0 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2021 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
Run PROGRAMS ARGS to compile a file, generating dependencies
as side-effects.
Environment variables:
depmode Dependency tracking mode.
source Source file read by 'PROGRAMS ARGS'.
object Object file output by 'PROGRAMS ARGS'.
DEPDIR directory where to store dependencies.
depfile Dependency file to output.
tmpdepfile Temporary file to use when outputting dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "depcomp $scriptversion"
exit $?
;;
esac
# Get the directory component of the given path, and save it in the
# global variables '$dir'. Note that this directory component will
# be either empty or ending with a '/' character. This is deliberate.
set_dir_from ()
{
case $1 in
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
*) dir=;;
esac
}
# Get the suffix-stripped basename of the given path, and save it the
# global variable '$base'.
set_base_from ()
{
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
}
# If no dependency file was actually created by the compiler invocation,
# we still have to create a dummy depfile, to avoid errors with the
# Makefile "include basename.Plo" scheme.
make_dummy_depfile ()
{
echo "#dummy" > "$depfile"
}
# Factor out some common post-processing of the generated depfile.
# Requires the auxiliary global variable '$tmpdepfile' to be set.
aix_post_process_depfile ()
{
# If the compiler actually managed to produce a dependency file,
# post-process it.
if test -f "$tmpdepfile"; then
# Each line is of the form 'foo.o: dependency.h'.
# Do two passes, one to just change these to
# $object: dependency.h
# and one to simply output
# dependency.h:
# which is needed to avoid the deleted-header problem.
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
} > "$depfile"
rm -f "$tmpdepfile"
else
make_dummy_depfile
fi
}
# A tabulation character.
tab=' '
# A newline character.
nl='
'
# Character ranges might be problematic outside the C locale.
# These definitions help.
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lower=abcdefghijklmnopqrstuvwxyz
digits=0123456789
alpha=${upper}${lower}
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
depfile=${depfile-`echo "$object" |
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Avoid interferences from the environment.
gccflag= dashmflag=
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
cygpath_u="cygpath -u -f -"
if test "$depmode" = msvcmsys; then
# This is just like msvisualcpp but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvisualcpp
fi
if test "$depmode" = msvc7msys; then
# This is just like msvc7 but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvc7
fi
if test "$depmode" = xlc; then
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
gccflag=-qmakedep=gcc,-MF
depmode=gcc
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
## the command line argument order; so add the flags where they
## appear in depend2.am. Note that the slowdown incurred here
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
for arg
do
case $arg in
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
*) set fnord "$@" "$arg" ;;
esac
shift # fnord
shift # $arg
done
"$@"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
## (see the conditional assignment to $gccflag above).
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say). Also, it might not be
## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The second -e expression handles DOS-style file names with drive
# letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the "deleted header file" problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
## Some versions of gcc put a space before the ':'. On the theory
## that the space means something, we add a space to the output as
## well. hp depmode also adds that space, but also prefixes the VPATH
## to the object. Take care to not repeat it in the output.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like '#:fec' to the end of the
# dependency line.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
| tr "$nl" ' ' >> "$depfile"
echo >> "$depfile"
# The second pass generates a dummy entry for each header file.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile"
;;
xlc)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. In older versions, this file always lives in the
# current directory. Also, the AIX compiler puts '$object:' at the
# start of each line; $object doesn't have directory information.
# Version 6 uses the directory in both cases.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.u
tmpdepfile2=$base.u
tmpdepfile3=$dir.libs/$base.u
"$@" -Wc,-M
else
tmpdepfile1=$dir$base.u
tmpdepfile2=$dir$base.u
tmpdepfile3=$dir$base.u
"$@" -M
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
aix_post_process_depfile
;;
tcc)
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
# FIXME: That version still under development at the moment of writing.
# Make that this statement remains true also for stable, released
# versions.
# It will wrap lines (doesn't matter whether long or short) with a
# trailing '\', as in:
#
# foo.o : \
# foo.c \
# foo.h \
#
# It will put a trailing '\' even on the last line, and will use leading
# spaces rather than leading tabs (at least since its commit 0394caf7
# "Emit spaces for -MD").
"$@" -MD -MF "$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
# We have to change lines of the first kind to '$object: \'.
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
# And for each line of the second kind, we have to emit a 'dep.h:'
# dummy dependency, to avoid the deleted-header problem.
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
rm -f "$tmpdepfile"
;;
## The order of this option in the case statement is important, since the
## shell code in configure will try each of these formats in the order
## listed in this file. A plain '-MD' option would be understood by many
## compilers, so we must ensure this comes after the gcc and icc options.
pgcc)
# Portland's C compiler understands '-MD'.
# Will always output deps to 'file.d' where file is the root name of the
# source file under compilation, even if file resides in a subdirectory.
# The object file name does not affect the name of the '.d' file.
# pgcc 10.2 will output
# foo.o: sub/foo.c sub/foo.h
# and will wrap long lines using '\' :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
set_dir_from "$object"
# Use the source, not the object, to determine the base name, since
# that's sadly what pgcc will do too.
set_base_from "$source"
tmpdepfile=$base.d
# For projects that build the same source file twice into different object
# files, the pgcc approach of using the *source* file root name can cause
# problems in parallel builds. Use a locking strategy to avoid stomping on
# the same $tmpdepfile.
lockdir=$base.d-lock
trap "
echo '$0: caught signal, cleaning up...' >&2
rmdir '$lockdir'
exit 1
" 1 2 13 15
numtries=100
i=$numtries
while test $i -gt 0; do
# mkdir is a portable test-and-set.
if mkdir "$lockdir" 2>/dev/null; then
# This process acquired the lock.
"$@" -MD
stat=$?
# Release the lock.
rmdir "$lockdir"
break
else
# If the lock is being held by a different process, wait
# until the winning process is done or we timeout.
while test -d "$lockdir" && test $i -gt 0; do
sleep 1
i=`expr $i - 1`
done
fi
i=`expr $i - 1`
done
trap - 1 2 13 15
if test $i -le 0; then
echo "$0: failed to acquire lock after $numtries attempts" >&2
echo "$0: check lockdir '$lockdir'" >&2
exit 1
fi
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each line is of the form `foo.o: dependent.h',
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp2)
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
# compilers, which have integrated preprocessors. The correct option
# to use with these is +Maked; it writes dependencies to a file named
# 'foo.d', which lands next to the object file, wherever that
# happens to be.
# Much of this is similar to the tru64 case; see comments there.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
"$@" -Wc,+Maked
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
"$@" +Maked
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
# Add 'dependent.h:' lines.
sed -ne '2,${
s/^ *//
s/ \\*$//
s/$/:/
p
}' "$tmpdepfile" >> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile" "$tmpdepfile2"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in 'foo.d' instead, so we check for that too.
# Subdirectories are respected.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
# Libtool generates 2 separate objects for the 2 libraries. These
# two compilations output dependencies in $dir.libs/$base.o.d and
# in $dir$base.o.d. We have to check for both files, because
# one of the two compilations can be disabled. We should prefer
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
# automatically cleaned when .libs/ is deleted, while ignoring
# the former would cause a distcleancheck panic.
tmpdepfile1=$dir$base.o.d # libtool 1.5
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
"$@" -Wc,-MD
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
tmpdepfile3=$dir$base.d
"$@" -MD
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
# Same post-processing that is required for AIX mode.
aix_post_process_depfile
;;
msvc7)
if test "$libtool" = yes; then
showIncludes=-Wc,-showIncludes
else
showIncludes=-showIncludes
fi
"$@" $showIncludes > "$tmpdepfile"
stat=$?
grep -v '^Note: including file: ' "$tmpdepfile"
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The first sed program below extracts the file names and escapes
# backslashes for cygpath. The second sed program outputs the file
# name when reading, but also accumulates all include files in the
# hold buffer in order to output them again at the end. This only
# works with sed implementations that can handle large buffers.
sed < "$tmpdepfile" -n '
/^Note: including file: *\(.*\)/ {
s//\1/
s/\\/\\\\/g
p
}' | $cygpath_u | sort -u | sed -n '
s/ /\\ /g
s/\(.*\)/'"$tab"'\1 \\/p
s/.\(.*\) \\/\1:/
H
$ {
s/.*/'"$tab"'/
G
p
}' >> "$depfile"
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
rm -f "$tmpdepfile"
;;
msvc7msys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
# Require at least two characters before searching for ':'
# in the target name. This is to cope with DOS-style filenames:
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
"$@" $dashmflag |
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this sed invocation
# correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# Remove any Libtool call
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# X makedepend
shift
cleared=no eat=no
for arg
do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
if test $eat = yes; then
eat=no
continue
fi
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
# Strip any option that makedepend may not understand. Remove
# the object too, otherwise makedepend will parse it as a source file.
-arch)
eat=yes ;;
-*|$object)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix=`echo "$object" | sed 's/^.*\././'`
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
# makedepend may prepend the VPATH from the source file name to the object.
# No need to regex-escape $object, excess matching of '.' is harmless.
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process the last invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed '1,2d' "$tmpdepfile" \
| tr ' ' "$nl" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E \
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
| sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
IFS=" "
for arg
do
case "$arg" in
-o)
shift
;;
$object)
shift
;;
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E 2>/dev/null |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
echo "$tab" >> "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvcmsys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

@ -1,541 +0,0 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2020-11-14.01; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
# Create dirs (including intermediate dirs) using mode 755.
# This is like GNU 'install' as of coreutils 8.32 (2020).
mkdir_umask=22
backupsuffix=
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-p pass -p to $cpprog.
-s $stripprog installed files.
-S SUFFIX attempt to back up existing files, with suffix SUFFIX.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
By default, rm is invoked with -f; when overridden with RMPROG,
it's up to you to specify -f if you want it.
If -S is not specified, no backups are attempted.
Email bug reports to bug-automake@gnu.org.
Automake home page: https://www.gnu.org/software/automake/
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-p) cpprog="$cpprog -p";;
-s) stripcmd=$stripprog;;
-S) backupsuffix="$2"
shift;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
# Don't chown directories that already exist.
if test $dstdir_status = 0; then
chowncmd=""
fi
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dstbase=`basename "$src"`
case $dst in
*/) dst=$dst$dstbase;;
*) dst=$dst/$dstbase;;
esac
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
case $dstdir in
*/) dstdirslash=$dstdir;;
*) dstdirslash=$dstdir/;;
esac
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
# The $RANDOM variable is not portable (e.g., dash). Use it
# here however when possible just to lower collision chance.
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap '
ret=$?
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
exit $ret
' 0
# Because "mkdir -p" follows existing symlinks and we likely work
# directly in world-writeable /tmp, make sure that the '$tmpdir'
# directory is successfully created first before we actually test
# 'mkdir -p'.
if (umask $mkdir_umask &&
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=${dstdirslash}_inst.$$_
rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask &&
{ test -z "$stripcmd" || {
# Create $dsttmp read-write so that cp doesn't create it read-only,
# which would cause strip to fail.
if test -z "$doit"; then
: >"$dsttmp" # No need to fork-exec 'touch'.
else
$doit touch "$dsttmp"
fi
}
} &&
$doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# If $backupsuffix is set, and the file being installed
# already exists, attempt a backup. Don't worry if it fails,
# e.g., if mv doesn't support -f.
if test -n "$backupsuffix" && test -f "$dst"; then
$doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
fi
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

@ -1,215 +0,0 @@
#! /bin/sh
# Common wrapper for a few potentially missing GNU programs.
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1996-2021 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try '$0 --help' for more information"
exit 1
fi
case $1 in
--is-lightweight)
# Used by our autoconf macros to check whether the available missing
# script is modern enough.
exit 0
;;
--run)
# Back-compat with the calling convention used by older automake.
shift
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
to PROGRAM being missing or too old.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
Supported PROGRAM values:
aclocal autoconf autoheader autom4te automake makeinfo
bison yacc flex lex help2man
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
'g' are ignored when checking the name.
Send bug reports to <bug-automake@gnu.org>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing $scriptversion (GNU Automake)"
exit $?
;;
-*)
echo 1>&2 "$0: unknown '$1' option"
echo 1>&2 "Try '$0 --help' for more information"
exit 1
;;
esac
# Run the given program, remember its exit status.
"$@"; st=$?
# If it succeeded, we are done.
test $st -eq 0 && exit 0
# Also exit now if we it failed (or wasn't found), and '--version' was
# passed; such an option is passed most likely to detect whether the
# program is present and works.
case $2 in --version|--help) exit $st;; esac
# Exit code 63 means version mismatch. This often happens when the user
# tries to use an ancient version of a tool on a file that requires a
# minimum version.
if test $st -eq 63; then
msg="probably too old"
elif test $st -eq 127; then
# Program was missing.
msg="missing on your system"
else
# Program was found and executed, but failed. Give up.
exit $st
fi
perl_URL=https://www.perl.org/
flex_URL=https://github.com/westes/flex
gnu_software_URL=https://www.gnu.org/software
program_details ()
{
case $1 in
aclocal|automake)
echo "The '$1' program is part of the GNU Automake package:"
echo "<$gnu_software_URL/automake>"
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/autoconf>"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
autoconf|autom4te|autoheader)
echo "The '$1' program is part of the GNU Autoconf package:"
echo "<$gnu_software_URL/autoconf/>"
echo "It also requires GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
esac
}
give_advice ()
{
# Normalize program name to check for.
normalized_program=`echo "$1" | sed '
s/^gnu-//; t
s/^gnu//; t
s/^g//; t'`
printf '%s\n' "'$1' is $msg."
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
case $normalized_program in
autoconf*)
echo "You should only need it if you modified 'configure.ac',"
echo "or m4 files included by it."
program_details 'autoconf'
;;
autoheader*)
echo "You should only need it if you modified 'acconfig.h' or"
echo "$configure_deps."
program_details 'autoheader'
;;
automake*)
echo "You should only need it if you modified 'Makefile.am' or"
echo "$configure_deps."
program_details 'automake'
;;
aclocal*)
echo "You should only need it if you modified 'acinclude.m4' or"
echo "$configure_deps."
program_details 'aclocal'
;;
autom4te*)
echo "You might have modified some maintainer files that require"
echo "the 'autom4te' program to be rebuilt."
program_details 'autom4te'
;;
bison*|yacc*)
echo "You should only need it if you modified a '.y' file."
echo "You may want to install the GNU Bison package:"
echo "<$gnu_software_URL/bison/>"
;;
lex*|flex*)
echo "You should only need it if you modified a '.l' file."
echo "You may want to install the Fast Lexical Analyzer package:"
echo "<$flex_URL>"
;;
help2man*)
echo "You should only need it if you modified a dependency" \
"of a man page."
echo "You may want to install the GNU Help2man package:"
echo "<$gnu_software_URL/help2man/>"
;;
makeinfo*)
echo "You should only need it if you modified a '.texi' file, or"
echo "any other file indirectly affecting the aspect of the manual."
echo "You might want to install the Texinfo package:"
echo "<$gnu_software_URL/texinfo/>"
echo "The spurious makeinfo call might also be the consequence of"
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
echo "want to install GNU make:"
echo "<$gnu_software_URL/make/>"
;;
*)
echo "You might have modified some files without having the proper"
echo "tools for further handling them. Check the 'README' file, it"
echo "often tells you about the needed prerequisites for installing"
echo "this package. You may also peek at any GNU archive site, in"
echo "case some other package contains this missing '$1' program."
;;
esac
}
give_advice "$1" | sed -e '1s/^/WARNING: /' \
-e '2,$s/^/ /' >&2
# Propagate the correct exit status (expected to be 127 for a program
# not found, 63 for a program that failed due to version mismatch).
exit $st
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

@ -1,4 +1,5 @@
/Makefile
/Makefile.in
/.deps/
*.o
*.lo

@ -20,8 +20,8 @@
#ifndef FFMPEG_KIT_ABSTRACT_SESSION_H
#define FFMPEG_KIT_ABSTRACT_SESSION_H
#import <Foundation/Foundation.h>
#import "Session.h"
#import <Foundation/Foundation.h>
/**
* Defines how long default "getAll" methods wait, in milliseconds.
@ -29,10 +29,11 @@
extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
/**
* Abstract session implementation which includes common features shared by <code>FFmpeg</code>,
* <code>FFprobe</code> and <code>MediaInformation</code> sessions.
* Abstract session implementation which includes common features shared by
* <code>FFmpeg</code>, <code>FFprobe</code> and <code>MediaInformation</code>
* sessions.
*/
@interface AbstractSession : NSObject<Session>
@interface AbstractSession : NSObject <Session>
/**
* Creates a new abstract session.
@ -41,10 +42,13 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
* @param logCallback session specific log callback
* @param logRedirectionStrategy session specific log redirection strategy
*/
- (instancetype)init:(NSArray*)arguments withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
- (instancetype)init:(NSArray *)arguments
withLogCallback:(LogCallback)logCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
/**
* Waits for all asynchronous messages to be transmitted until the given timeout.
* Waits for all asynchronous messages to be transmitted until the given
* timeout.
*
* @param timeout wait timeout in milliseconds
*/

@ -33,15 +33,15 @@ extern void addSessionToSessionHistory(id<Session> session);
@implementation AbstractSession {
long _sessionId;
LogCallback _logCallback;
NSDate* _createTime;
NSDate* _startTime;
NSDate* _endTime;
NSArray* _arguments;
NSMutableArray* _logs;
NSRecursiveLock* _logsLock;
NSDate *_createTime;
NSDate *_startTime;
NSDate *_endTime;
NSArray *_arguments;
NSMutableArray *_logs;
NSRecursiveLock *_logsLock;
SessionState _state;
ReturnCode* _returnCode;
NSString* _failStackTrace;
ReturnCode *_returnCode;
NSString *_failStackTrace;
LogRedirectionStrategy _logRedirectionStrategy;
}
@ -49,7 +49,9 @@ extern void addSessionToSessionHistory(id<Session> session);
sessionIdGenerator = [[AtomicLong alloc] initWithValue:1];
}
- (instancetype)init:(NSArray*)arguments withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
- (instancetype)init:(NSArray *)arguments
withLogCallback:(LogCallback)logCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
self = [super init];
if (self) {
_sessionId = [sessionIdGenerator getAndIncrement];
@ -79,86 +81,101 @@ extern void addSessionToSessionHistory(id<Session> session);
return _sessionId;
}
- (NSDate*)getCreateTime {
- (NSDate *)getCreateTime {
return _createTime;
}
- (NSDate*)getStartTime {
- (NSDate *)getStartTime {
return _startTime;
}
- (NSDate*)getEndTime {
- (NSDate *)getEndTime {
return _endTime;
}
- (long)getDuration {
NSDate* startTime = _startTime;
NSDate* endTime = _endTime;
NSDate *startTime = _startTime;
NSDate *endTime = _endTime;
if (startTime != nil && endTime != nil) {
return [[NSNumber numberWithDouble:([endTime timeIntervalSinceDate:startTime]*1000)] longValue];
return [[NSNumber
numberWithDouble:([endTime timeIntervalSinceDate:startTime] * 1000)]
longValue];
}
return 0;
}
- (NSArray*)getArguments {
- (NSArray *)getArguments {
return _arguments;
}
- (NSString*)getCommand {
- (NSString *)getCommand {
return [FFmpegKitConfig argumentsToString:_arguments];
}
- (void)waitForAsynchronousMessagesInTransmit:(int)timeout {
NSDate* expireDate = [[NSDate date] dateByAddingTimeInterval:((double)timeout)/1000];
NSDate *expireDate =
[[NSDate date] dateByAddingTimeInterval:((double)timeout) / 1000];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
while ([self thereAreAsynchronousMessagesInTransmit] && ([[NSDate date] timeIntervalSinceDate:expireDate] < 0)) {
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC));
while ([self thereAreAsynchronousMessagesInTransmit] &&
([[NSDate date] timeIntervalSinceDate:expireDate] < 0)) {
dispatch_semaphore_wait(
semaphore, dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC));
}
}
- (NSArray*)getAllLogsWithTimeout:(int)waitTimeout {
- (NSArray *)getAllLogsWithTimeout:(int)waitTimeout {
[self waitForAsynchronousMessagesInTransmit:waitTimeout];
if ([self thereAreAsynchronousMessagesInTransmit]) {
NSLog(@"getAllLogsWithTimeout was called to return all logs but there are still logs being transmitted for session id %ld.", _sessionId);
NSLog(@"getAllLogsWithTimeout was called to return all logs but there "
@"are "
@"still logs being transmitted for session id %ld.",
_sessionId);
}
return [self getLogs];
}
- (NSArray*)getAllLogs {
return [self getAllLogsWithTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
- (NSArray *)getAllLogs {
return [self
getAllLogsWithTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
}
- (NSArray*)getLogs {
- (NSArray *)getLogs {
[_logsLock lock];
NSArray* logsCopy = [_logs copy];
NSArray *logsCopy = [_logs copy];
[_logsLock unlock];
return logsCopy;
}
- (NSString*)getAllLogsAsStringWithTimeout:(int)waitTimeout {
- (NSString *)getAllLogsAsStringWithTimeout:(int)waitTimeout {
[self waitForAsynchronousMessagesInTransmit:waitTimeout];
if ([self thereAreAsynchronousMessagesInTransmit]) {
NSLog(@"getAllLogsAsStringWithTimeout was called to return all logs but there are still logs being transmitted for session id %ld.", _sessionId);
NSLog(
@"getAllLogsAsStringWithTimeout was called to return all logs but "
@"there are still logs being transmitted for session id %ld.",
_sessionId);
}
return [self getLogsAsString];
}
- (NSString*)getAllLogsAsString {
return [self getAllLogsAsStringWithTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
- (NSString *)getAllLogsAsString {
return [self
getAllLogsAsStringWithTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
}
- (NSString*)getLogsAsString {
NSMutableString* concatenatedString = [[NSMutableString alloc] init];
- (NSString *)getLogsAsString {
NSMutableString *concatenatedString = [[NSMutableString alloc] init];
[_logsLock lock];
for (int i=0; i < [_logs count]; i++) {
for (int i = 0; i < [_logs count]; i++) {
[concatenatedString appendString:[[_logs objectAtIndex:i] getMessage]];
}
[_logsLock unlock];
@ -166,7 +183,7 @@ extern void addSessionToSessionHistory(id<Session> session);
return concatenatedString;
}
- (NSString*)getOutput {
- (NSString *)getOutput {
return [self getAllLogsAsString];
}
@ -174,11 +191,11 @@ extern void addSessionToSessionHistory(id<Session> session);
return _state;
}
- (ReturnCode*)getReturnCode {
- (ReturnCode *)getReturnCode {
return _returnCode;
}
- (NSString*)getFailStackTrace {
- (NSString *)getFailStackTrace {
return _failStackTrace;
}
@ -190,7 +207,7 @@ extern void addSessionToSessionHistory(id<Session> session);
return ([FFmpegKitConfig messagesInTransmit:_sessionId] != 0);
}
- (void)addLog:(Log*)log {
- (void)addLog:(Log *)log {
[_logsLock lock];
[_logs addObject:log];
[_logsLock unlock];
@ -201,14 +218,16 @@ extern void addSessionToSessionHistory(id<Session> session);
_startTime = [NSDate date];
}
- (void)complete:(ReturnCode*)returnCode {
- (void)complete:(ReturnCode *)returnCode {
_returnCode = returnCode;
_state = SessionStateCompleted;
_endTime = [NSDate date];
}
- (void)fail:(NSException*)exception {
_failStackTrace = [NSString stringWithFormat:@"%@\n%@", [exception userInfo], [exception callStackSymbols]];
- (void)fail:(NSException *)exception {
_failStackTrace =
[NSString stringWithFormat:@"%@\n%@", [exception userInfo],
[exception callStackSymbols]];
_state = SessionStateFailed;
_endTime = [NSDate date];
}

@ -32,14 +32,21 @@
*
* @return architecture name of the cpu running
*/
+ (NSString*)getCpuArch;
+ (NSString *)getCpuArch;
/**
* Returns architecture name loaded.
*
* @return architecture name loaded
*/
+ (NSString*)getArch;
+ (NSString *)getArch;
/**
* Returns the minimum skd version required to run this native library
*
* @return the minimum skd version required to run this native library
*/
+ (NSString *)getMinSdk;
@end

@ -17,13 +17,13 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#import <sys/types.h>
#import <sys/sysctl.h>
#import <mach/machine.h>
#import "ArchDetect.h"
#import "FFmpegKitConfig.h"
#import "FFmpegKit.h"
#import "FFmpegKitConfig.h"
#import "FFprobeKit.h"
#import <mach/machine.h>
#import <sys/sysctl.h>
#import <sys/types.h>
@implementation ArchDetect
@ -33,8 +33,8 @@
[FFprobeKit class];
}
+ (NSString*)getCpuArch {
NSMutableString* cpu = [[NSMutableString alloc] init];
+ (NSString *)getCpuArch {
NSMutableString *cpu = [[NSMutableString alloc] init];
size_t size;
cpu_type_t type;
cpu_subtype_t subtype;
@ -50,15 +50,15 @@
} else if (type == CPU_TYPE_X86) {
[cpu appendString:@"x86"];
switch(subtype) {
case CPU_SUBTYPE_X86_64_H:
[cpu appendString:@"_64h"];
switch (subtype) {
case CPU_SUBTYPE_X86_64_H:
[cpu appendString:@"_64h"];
break;
case CPU_SUBTYPE_X86_64_ALL:
[cpu appendString:@"_64all"];
case CPU_SUBTYPE_X86_64_ALL:
[cpu appendString:@"_64all"];
break;
case CPU_SUBTYPE_X86_ARCH1:
[cpu appendString:@"_arch1"];
case CPU_SUBTYPE_X86_ARCH1:
[cpu appendString:@"_arch1"];
break;
}
@ -68,73 +68,74 @@
} else if (type == CPU_TYPE_ARM64) {
[cpu appendString:@"arm64"];
switch(subtype) {
case CPU_SUBTYPE_ARM64_V8:
[cpu appendString:@"v8"];
switch (subtype) {
case CPU_SUBTYPE_ARM64_V8:
[cpu appendString:@"v8"];
break;
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120100
case CPU_SUBTYPE_ARM64E:
[cpu appendString:@"e"];
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= 120100
case CPU_SUBTYPE_ARM64E:
[cpu appendString:@"e"];
break;
#endif
#endif
}
} else if (type == CPU_TYPE_ARM) {
[cpu appendString:@"arm"];
switch(subtype) {
case CPU_SUBTYPE_ARM_XSCALE:
[cpu appendString:@"xscale"];
switch (subtype) {
case CPU_SUBTYPE_ARM_XSCALE:
[cpu appendString:@"xscale"];
break;
case CPU_SUBTYPE_ARM_V4T:
[cpu appendString:@"v4t"];
case CPU_SUBTYPE_ARM_V4T:
[cpu appendString:@"v4t"];
break;
case CPU_SUBTYPE_ARM_V5TEJ:
[cpu appendString:@"v5tej"];
case CPU_SUBTYPE_ARM_V5TEJ:
[cpu appendString:@"v5tej"];
break;
case CPU_SUBTYPE_ARM_V6:
[cpu appendString:@"v6"];
case CPU_SUBTYPE_ARM_V6:
[cpu appendString:@"v6"];
break;
case CPU_SUBTYPE_ARM_V6M:
[cpu appendString:@"v6m"];
case CPU_SUBTYPE_ARM_V6M:
[cpu appendString:@"v6m"];
break;
case CPU_SUBTYPE_ARM_V7:
[cpu appendString:@"v7"];
case CPU_SUBTYPE_ARM_V7:
[cpu appendString:@"v7"];
break;
case CPU_SUBTYPE_ARM_V7EM:
[cpu appendString:@"v7em"];
case CPU_SUBTYPE_ARM_V7EM:
[cpu appendString:@"v7em"];
break;
case CPU_SUBTYPE_ARM_V7F:
[cpu appendString:@"v7f"];
case CPU_SUBTYPE_ARM_V7F:
[cpu appendString:@"v7f"];
break;
case CPU_SUBTYPE_ARM_V7K:
[cpu appendString:@"v7k"];
case CPU_SUBTYPE_ARM_V7K:
[cpu appendString:@"v7k"];
break;
case CPU_SUBTYPE_ARM_V7M:
[cpu appendString:@"v7m"];
case CPU_SUBTYPE_ARM_V7M:
[cpu appendString:@"v7m"];
break;
case CPU_SUBTYPE_ARM_V7S:
[cpu appendString:@"v7s"];
case CPU_SUBTYPE_ARM_V7S:
[cpu appendString:@"v7s"];
break;
#ifndef FFMPEG_KIT_LTS
case CPU_SUBTYPE_ARM_V8:
[cpu appendString:@"v8"];
case CPU_SUBTYPE_ARM_V8:
[cpu appendString:@"v8"];
break;
#endif
}
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120100
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= 120100
} else if (type == CPU_TYPE_ARM64_32) {
[cpu appendString:@"arm64_32"];
switch(subtype) {
case CPU_SUBTYPE_ARM64_32_V8:
[cpu appendString:@"v8"];
switch (subtype) {
case CPU_SUBTYPE_ARM64_32_V8:
[cpu appendString:@"v8"];
break;
}
#endif
#endif
} else {
[cpu appendString:[NSString stringWithFormat:@"%d", type]];
@ -143,7 +144,7 @@
return cpu;
}
+ (NSString*)getArch {
+ (NSString *)getArch {
NSMutableString *arch = [[NSMutableString alloc] init];
#ifdef FFMPEG_KIT_ARMV7
@ -169,4 +170,12 @@
return arch;
}
+ (NSString *)getMinSdk {
#ifdef FFMPEG_KIT_MIN_SDK
return [NSString stringWithUTF8String:FFMPEG_KIT_MIN_SDK];
#else
return @"";
#endif
}
@end

@ -17,8 +17,8 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#import <stdatomic.h>
#import "AtomicLong.h"
#import <stdatomic.h>
@implementation AtomicLong {
atomic_int _value;

@ -22,62 +22,63 @@
#import <Foundation/Foundation.h>
extern NSString* const ChapterKeyId;
extern NSString* const ChapterKeyTimeBase;
extern NSString* const ChapterKeyStart;
extern NSString* const ChapterKeyStartTime;
extern NSString* const ChapterKeyEnd;
extern NSString* const ChapterKeyEndTime;
extern NSString* const ChapterKeyTags;
extern NSString *const ChapterKeyId;
extern NSString *const ChapterKeyTimeBase;
extern NSString *const ChapterKeyStart;
extern NSString *const ChapterKeyStartTime;
extern NSString *const ChapterKeyEnd;
extern NSString *const ChapterKeyEndTime;
extern NSString *const ChapterKeyTags;
/**
* Chapter class.
*/
@interface Chapter : NSObject
- (instancetype)init:(NSDictionary*)chapterDictionary;
- (instancetype)init:(NSDictionary *)chapterDictionary;
- (NSNumber*)getId;
- (NSNumber *)getId;
- (NSString*)getTimeBase;
- (NSString *)getTimeBase;
- (NSNumber*)getStart;
- (NSNumber *)getStart;
- (NSString*)getStartTime;
- (NSString *)getStartTime;
- (NSNumber*)getEnd;
- (NSNumber *)getEnd;
- (NSString*)getEndTime;
- (NSString *)getEndTime;
- (NSDictionary*)getTags;
- (NSDictionary *)getTags;
/**
* Returns the chapter property associated with the key.
*
* @return chapter property as string or nil if the key is not found
*/
- (NSString*)getStringProperty:(NSString*)key;
- (NSString *)getStringProperty:(NSString *)key;
/**
* Returns the chapter property associated with the key.
*
* @return chapter property as number or nil if the key is not found
*/
- (NSNumber*)getNumberProperty:(NSString*)key;
- (NSNumber *)getNumberProperty:(NSString *)key;
/**
* Returns the chapter property associated with the key.
*
* @return chapter property as id or nil if the key is not found
*/
- (id)getProperty:(NSString*)key;
*/
- (id)getProperty:(NSString *)key;
/**
* Returns all chapter properties defined.
*
* @return all chapter properties in a dictionary or nil if no properties are defined
*/
- (NSDictionary*)getAllProperties;
* @return all chapter properties in a dictionary or nil if no properties are
* defined
*/
- (NSDictionary *)getAllProperties;
@end

@ -19,13 +19,13 @@
#import "Chapter.h"
NSString* const ChapterKeyId = @"id";
NSString* const ChapterKeyTimeBase = @"time_base";
NSString* const ChapterKeyStart = @"start";
NSString* const ChapterKeyStartTime = @"start_time";
NSString* const ChapterKeyEnd = @"end";
NSString* const ChapterKeyEndTime = @"end_time";
NSString* const ChapterKeyTags = @"tags";
NSString *const ChapterKeyId = @"id";
NSString *const ChapterKeyTimeBase = @"time_base";
NSString *const ChapterKeyStart = @"start";
NSString *const ChapterKeyStartTime = @"start_time";
NSString *const ChapterKeyEnd = @"end";
NSString *const ChapterKeyEndTime = @"end_time";
NSString *const ChapterKeyTags = @"tags";
@implementation Chapter {
@ -33,10 +33,9 @@ NSString* const ChapterKeyTags = @"tags";
* Stores all properties.
*/
NSDictionary *dictionary;
}
- (instancetype)init:(NSDictionary*)chapterDictionary {
- (instancetype)init:(NSDictionary *)chapterDictionary {
self = [super init];
if (self) {
dictionary = chapterDictionary;
@ -45,36 +44,36 @@ NSString* const ChapterKeyTags = @"tags";
return self;
}
- (NSNumber*)getId {
- (NSNumber *)getId {
return [self getNumberProperty:ChapterKeyId];
}
- (NSString*)getTimeBase {
- (NSString *)getTimeBase {
return [self getStringProperty:ChapterKeyTimeBase];
}
- (NSNumber*)getStart {
- (NSNumber *)getStart {
return [self getNumberProperty:ChapterKeyStart];
}
- (NSString*)getStartTime {
- (NSString *)getStartTime {
return [self getStringProperty:ChapterKeyStartTime];
}
- (NSNumber*)getEnd {
- (NSNumber *)getEnd {
return [self getNumberProperty:ChapterKeyEnd];
}
- (NSString*)getEndTime {
- (NSString *)getEndTime {
return [self getStringProperty:ChapterKeyEndTime];
}
- (NSDictionary*)getTags {
- (NSDictionary *)getTags {
return [self getProperty:ChapterKeyTags];
}
- (NSString*)getStringProperty:(NSString*)key {
NSDictionary* allProperties = [self getAllProperties];
- (NSString *)getStringProperty:(NSString *)key {
NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) {
return nil;
}
@ -82,8 +81,8 @@ NSString* const ChapterKeyTags = @"tags";
return allProperties[key];
}
- (NSNumber*)getNumberProperty:(NSString*)key {
NSDictionary* allProperties = [self getAllProperties];
- (NSNumber *)getNumberProperty:(NSString *)key {
NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) {
return nil;
}
@ -91,8 +90,8 @@ NSString* const ChapterKeyTags = @"tags";
return allProperties[key];
}
- (id)getProperty:(NSString*)key {
NSDictionary* allProperties = [self getAllProperties];
- (id)getProperty:(NSString *)key {
NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) {
return nil;
}
@ -100,7 +99,7 @@ NSString* const ChapterKeyTags = @"tags";
return allProperties[key];
}
- (NSDictionary*)getAllProperties {
- (NSDictionary *)getAllProperties {
return dictionary;
}

@ -20,24 +20,26 @@
#ifndef FFMPEG_KIT_H
#define FFMPEG_KIT_H
#import <string.h>
#import <stdlib.h>
#import <Foundation/Foundation.h>
#import "LogCallback.h"
#import "FFmpegSession.h"
#import "LogCallback.h"
#import "StatisticsCallback.h"
#import <Foundation/Foundation.h>
#import <stdlib.h>
#import <string.h>
/**
* <p>Main class to run <code>FFmpeg</code> commands. Supports executing commands both synchronously and
* asynchronously.
* <pre>
* FFmpegSession *session = [FFmpegKit execute:@"-i file1.mp4 -c:v libxvid file1.avi"];
* <p>Main class to run <code>FFmpeg</code> commands. Supports executing
* commands both synchronously and asynchronously. <pre> FFmpegSession *session
* = [FFmpegKit execute:@"-i file1.mp4 -c:v libxvid file1.avi"];
*
* FFmpegSession *asyncSession = [FFmpegKit executeAsync:@"-i file1.mp4 -c:v libxvid file1.avi" withCompleteCallback:completeCallback];
* FFmpegSession *asyncSession = [FFmpegKit executeAsync:@"-i file1.mp4 -c:v
* libxvid file1.avi" withCompleteCallback:completeCallback];
* </pre>
* <p>Provides overloaded <code>execute</code> methods to define session specific callbacks.
* <pre>
* FFmpegSession *asyncSession = [FFmpegKit executeAsync:@"-i file1.mp4 -c:v libxvid file1.avi" withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
* <p>Provides overloaded <code>execute</code> methods to define session
* specific callbacks. <pre> FFmpegSession *asyncSession = [FFmpegKit
* executeAsync:@"-i file1.mp4 -c:v libxvid file1.avi"
* withCompleteCallback:completeCallback withLogCallback:logCallback
* withStatisticsCallback:statisticsCallback];
* </pre>
*/
@interface FFmpegKit : NSObject
@ -48,141 +50,191 @@
* @param arguments FFmpeg command options/arguments as string array
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArguments:(NSArray*)arguments;
+ (FFmpegSession *)executeWithArguments:(NSArray *)arguments;
/**
* <p>Starts an asynchronous FFmpeg execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param arguments FFmpeg command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution has
* completed
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback;
+ (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFmpeg execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param arguments FFmpeg command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution
* has completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback;
+ (FFmpegSession *)
executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* <p>Starts an asynchronous FFmpeg execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param arguments FFmpeg command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param completeCallback callback that will be called when the execution has
* completed
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFmpeg execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param arguments FFmpeg command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution
* has completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (FFmpegSession *)
executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Synchronously executes FFmpeg command provided. Space character is used to split command
* into arguments. You can use single or double quote characters to specify arguments inside
* your command.
* <p>Synchronously executes FFmpeg command provided. Space character is used to
* split command into arguments. You can use single or double quote characters
* to specify arguments inside your command.
*
* @param command FFmpeg command
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)execute:(NSString*)command;
+ (FFmpegSession *)execute:(NSString *)command;
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
* <p>Starts an asynchronous FFmpeg execution for the given command. Space
* character is used to split the command into arguments. You can use single or
* double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param command FFmpeg command
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution has
* completed
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback;
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
* <p>Starts an asynchronous FFmpeg execution for the given command. Space
* character is used to split the command into arguments. You can use single or
* double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param command FFmpeg command
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution
* has completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback;
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
* <p>Starts an asynchronous FFmpeg execution for the given command. Space
* character is used to split the command into arguments. You can use single or
* double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param command FFmpeg command
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param completeCallback callback that will be called when the execution has
* completed
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
* <p>Starts an asynchronous FFmpeg execution for the given command. Space
* character is used to split the command into arguments. You can use single or
* double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param command FFmpeg command
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution
* has completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return FFmpeg session created for this execution
*/
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Cancels all running sessions.
*
* <p>This method does not wait for termination to complete and returns immediately.
* <p>This method does not wait for termination to complete and returns
* immediately.
*/
+ (void)cancel;
/**
* <p>Cancels the session specified with <code>sessionId</code>.
*
* <p>This method does not wait for termination to complete and returns immediately.
* <p>This method does not wait for termination to complete and returns
* immediately.
*
* @param sessionId id of the session that will be cancelled
*/
@ -193,7 +245,7 @@
*
* @return all FFmpeg sessions in the session history
*/
+ (NSArray*)listSessions;
+ (NSArray *)listSessions;
@end

@ -31,65 +31,123 @@
[FFmpegKitConfig class];
NSLog(@"Loaded ffmpeg-kit-%@-%@-%@-%@.\n", [Packages getPackageName], [ArchDetect getArch], [FFmpegKitConfig getVersion], [FFmpegKitConfig getBuildDate]);
#if TARGET_OS_MACCATALYST
NSString* osType = @"maccatalyst";
#elif TARGET_OS_TV
NSString* osType = @"tvos";
#elif TARGET_OS_IOS
NSString* osType = @"ios";
#elif TARGET_OS_MAC
NSString* osType = @"macos";
#endif
NSLog(@"Loaded ffmpeg-kit-%@-%@-%@-%@%@-%@.\n", [Packages getPackageName],
[ArchDetect getArch], [FFmpegKitConfig getVersion], osType,
[ArchDetect getMinSdk], [FFmpegKitConfig getBuildDate]);
}
+ (FFmpegSession*)executeWithArguments:(NSArray*)arguments {
FFmpegSession* session = [FFmpegSession create:arguments];
+ (FFmpegSession *)executeWithArguments:(NSArray *)arguments {
FFmpegSession *session = [FFmpegSession create:arguments];
[FFmpegKitConfig ffmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback];
+ (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
+ (FFmpegSession *)
executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback];
+ (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session;
}
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
+ (FFmpegSession *)
executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session;
}
+ (FFmpegSession*)execute:(NSString*)command {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command]];
+ (FFmpegSession *)execute:(NSString *)command {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig ffmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback];
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback];
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session;
}
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session;
}
@ -98,7 +156,8 @@
/*
* ZERO (0) IS A SPECIAL SESSION ID
* WHEN IT IS PASSED TO THIS METHOD, A SIGINT IS GENERATED WHICH CANCELS ALL ONGOING SESSIONS
* WHEN IT IS PASSED TO THIS METHOD, A SIGINT IS GENERATED WHICH CANCELS ALL
* ONGOING SESSIONS
*/
cancel_operation(0);
}
@ -107,7 +166,7 @@
cancel_operation(sessionId);
}
+ (NSArray*)listSessions {
+ (NSArray *)listSessions {
return [FFmpegKitConfig getFFmpegSessions];
}

@ -20,18 +20,18 @@
#ifndef FFMPEG_KIT_CONFIG_H
#define FFMPEG_KIT_CONFIG_H
#import <stdio.h>
#import <pthread.h>
#import <unistd.h>
#import <Foundation/Foundation.h>
#import "FFmpegSession.h"
#import "FFprobeSession.h"
#import "LogCallback.h"
#import "MediaInformationSession.h"
#import "StatisticsCallback.h"
#import <Foundation/Foundation.h>
#import <pthread.h>
#import <stdio.h>
#import <unistd.h>
/** Global library version */
extern NSString* const FFmpegKitVersion;
extern NSString *const FFmpegKitVersion;
typedef NS_ENUM(NSUInteger, Signal) {
SignalInt = 2,
@ -42,29 +42,31 @@ typedef NS_ENUM(NSUInteger, Signal) {
};
/**
* <p>Configuration class of <code>FFmpegKit</code> library. Allows customizing the global library
* options. Provides helper methods to support additional resources.
* <p>Configuration class of <code>FFmpegKit</code> library. Allows customizing
* the global library options. Provides helper methods to support additional
* resources.
*/
@interface FFmpegKitConfig : NSObject
/**
* <p>Enables log and statistics redirection.
*
* <p>When redirection is enabled FFmpeg/FFprobe logs are redirected to NSLog and sessions
* collect log and statistics entries for the executions. It is possible to define global or
* session specific log/statistics callbacks as well.
* <p>When redirection is enabled FFmpeg/FFprobe logs are redirected to NSLog
* and sessions collect log and statistics entries for the executions. It is
* possible to define global or session specific log/statistics callbacks as
* well.
*
* <p>Note that redirection is enabled by default. If you do not want to use its functionality
* please use disableRedirection method to disable it.
* <p>Note that redirection is enabled by default. If you do not want to use its
* functionality please use disableRedirection method to disable it.
*/
+ (void)enableRedirection;
/**
* <p>Disables log and statistics redirection.
*
* <p>When redirection is disabled logs are printed to stderr, all logs and statistics
* callbacks are disabled and <code>FFprobe</code>'s <code>getMediaInformation</code> methods
* do not work.
* <p>When redirection is disabled logs are printed to stderr, all logs and
* statistics callbacks are disabled and <code>FFprobe</code>'s
* <code>getMediaInformation</code> methods do not work.
*/
+ (void)disableRedirection;
@ -74,35 +76,40 @@ typedef NS_ENUM(NSUInteger, Signal) {
* @param path directory that contains fontconfig configuration (fonts.conf)
* @return zero on success, non-zero on error
*/
+ (int)setFontconfigConfigurationPath:(NSString*)path;
+ (int)setFontconfigConfigurationPath:(NSString *)path;
/**
* <p>Registers the fonts inside the given path, so they become available to use in FFmpeg
* filters.
* <p>Registers the fonts inside the given path, so they become available to use
* in FFmpeg filters.
*
* <p>Note that you need to build <code>FFmpegKit</code> with <code>fontconfig</code>
* enabled or use a prebuilt package with <code>fontconfig</code> inside to be able to use
* fonts in <code>FFmpeg</code>.
* <p>Note that you need to build <code>FFmpegKit</code> with
* <code>fontconfig</code> enabled or use a prebuilt package with
* <code>fontconfig</code> inside to be able to use fonts in
* <code>FFmpeg</code>.
*
* @param fontDirectoryPath directory that contains fonts (.ttf and .otf files)
* @param fontNameMapping custom font name mappings, useful to access your fonts with more
* friendly names
* @param fontNameMapping custom font name mappings, useful to access your
* fonts with more friendly names
*/
+ (void)setFontDirectory:(NSString*)fontDirectoryPath with:(NSDictionary*)fontNameMapping;
+ (void)setFontDirectory:(NSString *)fontDirectoryPath
with:(NSDictionary *)fontNameMapping;
/**
* <p>Registers the fonts inside the given array of font directories, so they become available
* to use in FFmpeg filters.
* <p>Registers the fonts inside the given array of font directories, so they
* become available to use in FFmpeg filters.
*
* <p>Note that you need to build <code>FFmpegKit</code> with <code>fontconfig</code>
* enabled or use a prebuilt package with <code>fontconfig</code> inside to be able to use
* fonts in <code>FFmpeg</code>.
* <p>Note that you need to build <code>FFmpegKit</code> with
* <code>fontconfig</code> enabled or use a prebuilt package with
* <code>fontconfig</code> inside to be able to use fonts in
* <code>FFmpeg</code>.
*
* @param fontDirectoryList array of directories that contain fonts (.ttf and .otf files)
* @param fontNameMapping custom font name mappings, useful to access your fonts with more
* friendly names
* @param fontDirectoryList array of directories that contain fonts (.ttf and
* .otf files)
* @param fontNameMapping custom font name mappings, useful to access your
* fonts with more friendly names
*/
+ (void)setFontDirectoryList:(NSArray*)fontDirectoryList with:(NSDictionary*)fontNameMapping;
+ (void)setFontDirectoryList:(NSArray *)fontDirectoryList
with:(NSDictionary *)fontNameMapping;
/**
* <p>Creates a new named pipe to use in <code>FFmpeg</code> operations.
@ -111,42 +118,43 @@ typedef NS_ENUM(NSUInteger, Signal) {
*
* @return the full path of the named pipe
*/
+ (NSString*)registerNewFFmpegPipe;
+ (NSString *)registerNewFFmpegPipe;
/**
* <p>Closes a previously created <code>FFmpeg</code> pipe.
*
* @param ffmpegPipePath full path of the FFmpeg pipe
*/
+ (void)closeFFmpegPipe:(NSString*)ffmpegPipePath;
+ (void)closeFFmpegPipe:(NSString *)ffmpegPipePath;
/**
* <p>Returns the version of FFmpeg bundled within <code>FFmpegKit</code> library.
* <p>Returns the version of FFmpeg bundled within <code>FFmpegKit</code>
* library.
*
* @return the version of FFmpeg
*/
+ (NSString*)getFFmpegVersion;
+ (NSString *)getFFmpegVersion;
/**
* Returns FFmpegKit library version.
*
* @return FFmpegKit version
*/
+ (NSString*)getVersion;
+ (NSString *)getVersion;
/**
* <p>Returns whether FFmpegKit release is a Long Term Release or not.
*
* @return true/yes or false/no
*/
+ (int)isLTSBuild;
+ (int)isLTSBuild __attribute__((deprecated("Deprecated as of version 6.1.2. Use AbiDetect.getMinSdk() method to determine the features supported by this version.")));
/**
* Returns FFmpegKit library build date.
*
* @return FFmpegKit library build date
*/
+ (NSString*)getBuildDate;
+ (NSString *)getBuildDate;
/**
* <p>Sets an environment variable.
@ -155,11 +163,12 @@ typedef NS_ENUM(NSUInteger, Signal) {
* @param variableValue environment variable value
* @return zero on success, non-zero on error
*/
+ (int)setEnvironmentVariable:(NSString*)variableName value:(NSString*)variableValue;
+ (int)setEnvironmentVariable:(NSString *)variableName
value:(NSString *)variableValue;
/**
* <p>Registers a new ignored signal. Ignored signals are not handled by <code>FFmpegKit</code>
* library.
* <p>Registers a new ignored signal. Ignored signals are not handled by
* <code>FFmpegKit</code> library.
*
* @param signal signal to be ignored
*/
@ -170,108 +179,143 @@ typedef NS_ENUM(NSUInteger, Signal) {
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
*/
+ (void)ffmpegExecute:(FFmpegSession*)ffmpegSession;
+ (void)ffmpegExecute:(FFmpegSession *)ffmpegSession;
/**
* <p>Synchronously executes the FFprobe session provided.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
* @param ffprobeSession FFprobe session which includes command
* options/arguments
*/
+ (void)ffprobeExecute:(FFprobeSession*)ffprobeSession;
+ (void)ffprobeExecute:(FFprobeSession *)ffprobeSession;
/**
* <p>Synchronously executes the media information session provided.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param waitTimeout max time to wait until media information is transmitted
* @param mediaInformationSession media information session which includes
* command options/arguments
* @param waitTimeout max time to wait until media information is
* transmitted
*/
+ (void)getMediaInformationExecute:(MediaInformationSession*)mediaInformationSession withTimeout:(int)waitTimeout;
+ (void)getMediaInformationExecute:
(MediaInformationSession *)mediaInformationSession
withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFmpeg execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
*/
+ (void)asyncFFmpegExecute:(FFmpegSession*)ffmpegSession;
+ (void)asyncFFmpegExecute:(FFmpegSession *)ffmpegSession;
/**
* <p>Starts an asynchronous FFmpeg execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param ffmpegSession FFmpeg session which includes command
* options/arguments
* @param queue dispatch queue that will be used to run this
* asynchronous operation
*/
+ (void)asyncFFmpegExecute:(FFmpegSession*)ffmpegSession onDispatchQueue:(dispatch_queue_t)queue;
+ (void)asyncFFmpegExecute:(FFmpegSession *)ffmpegSession
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
* @param ffprobeSession FFprobe session which includes command
* options/arguments
*/
+ (void)asyncFFprobeExecute:(FFprobeSession*)ffprobeSession;
+ (void)asyncFFprobeExecute:(FFprobeSession *)ffprobeSession;
/**
* <p>Starts an asynchronous FFprobe execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param ffprobeSession FFprobe session which includes command
* options/arguments
* @param queue dispatch queue that will be used to run this
* asynchronous operation
*/
+ (void)asyncFFprobeExecute:(FFprobeSession*)ffprobeSession onDispatchQueue:(dispatch_queue_t)queue;
+ (void)asyncFFprobeExecute:(FFprobeSession *)ffprobeSession
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution for the given media information session.
* <p>Starts an asynchronous FFprobe execution for the given media information
* session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an MediaInformationSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param waitTimeout max time to wait until media information is transmitted
* @param mediaInformationSession media information session which includes
* command options/arguments
* @param waitTimeout max time to wait until media information is
* transmitted
*/
+ (void)asyncGetMediaInformationExecute:(MediaInformationSession*)mediaInformationSession withTimeout:(int)waitTimeout;
+ (void)asyncGetMediaInformationExecute:
(MediaInformationSession *)mediaInformationSession
withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution for the given media information session.
* <p>Starts an asynchronous FFprobe execution for the given media information
* session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an MediaInformationSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
* @param mediaInformationSession media information session which includes
* command options/arguments
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @param waitTimeout max time to wait until media information is
* transmitted
*/
+ (void)asyncGetMediaInformationExecute:(MediaInformationSession*)mediaInformationSession onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
+ (void)asyncGetMediaInformationExecute:
(MediaInformationSession *)mediaInformationSession
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout;
/**
* <p>Sets a global log callback to redirect FFmpeg/FFprobe logs.
*
* @param logCallback log callback or nil to disable a previously defined log callback
* @param logCallback log callback or nil to disable a previously defined log
* callback
*/
+ (void)enableLogCallback:(LogCallback)logCallback;
/**
* <p>Sets a global statistics callback to redirect FFmpeg statistics.
*
* @param statisticsCallback statistics callback or nil to disable a previously defined statistics callback
* @param statisticsCallback statistics callback or nil to disable a previously
* defined statistics callback
*/
+ (void)enableStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* <p>Sets a global FFmpegSessionCompleteCallback to receive execution results for FFmpeg sessions.
* <p>Sets a global FFmpegSessionCompleteCallback to receive execution results
* for FFmpeg sessions.
*
* @param ffmpegSessionCompleteCallback complete callback or nil to disable a previously defined callback
* @param ffmpegSessionCompleteCallback complete callback or nil to disable a
* previously defined callback
*/
+ (void)enableFFmpegSessionCompleteCallback:(FFmpegSessionCompleteCallback)ffmpegSessionCompleteCallback;
+ (void)enableFFmpegSessionCompleteCallback:
(FFmpegSessionCompleteCallback)ffmpegSessionCompleteCallback;
/**
* <p>Returns the global FFmpegSessionCompleteCallback set.
@ -281,11 +325,14 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (FFmpegSessionCompleteCallback)getFFmpegSessionCompleteCallback;
/**
* <p>Sets a global FFprobeSessionCompleteCallback to receive execution results for FFprobe sessions.
* <p>Sets a global FFprobeSessionCompleteCallback to receive execution results
* for FFprobe sessions.
*
* @param ffprobeSessionCompleteCallback complete callback or nil to disable a previously defined callback
* @param ffprobeSessionCompleteCallback complete callback or nil to disable a
* previously defined callback
*/
+ (void)enableFFprobeSessionCompleteCallback:(FFprobeSessionCompleteCallback)ffprobeSessionCompleteCallback;
+ (void)enableFFprobeSessionCompleteCallback:
(FFprobeSessionCompleteCallback)ffprobeSessionCompleteCallback;
/**
* <p>Returns the global FFprobeSessionCompleteCallback set.
@ -295,19 +342,24 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (FFprobeSessionCompleteCallback)getFFprobeSessionCompleteCallback;
/**
* <p>Sets a global MediaInformationSessionCompleteCallback to receive execution results for MediaInformation sessions.
* <p>Sets a global MediaInformationSessionCompleteCallback to receive execution
* results for MediaInformation sessions.
*
* @param mediaInformationSessionCompleteCallback complete callback or nil to disable a previously defined
* callback
* @param mediaInformationSessionCompleteCallback complete callback or nil to
* disable a previously defined callback
*/
+ (void)enableMediaInformationSessionCompleteCallback:(MediaInformationSessionCompleteCallback)mediaInformationSessionCompleteCallback;
+ (void)enableMediaInformationSessionCompleteCallback:
(MediaInformationSessionCompleteCallback)
mediaInformationSessionCompleteCallback;
/**
* <p>Returns the global MediaInformationSessionCompleteCallback set.
*
* @return global MediaInformationSessionCompleteCallback or nil if it is not set
* @return global MediaInformationSessionCompleteCallback or nil if it is not
* set
*/
+ (MediaInformationSessionCompleteCallback)getMediaInformationSessionCompleteCallback;
+ (MediaInformationSessionCompleteCallback)
getMediaInformationSessionCompleteCallback;
/**
* Returns the current log level.
@ -329,7 +381,7 @@ typedef NS_ENUM(NSUInteger, Signal) {
* @param level value
* @return string value
*/
+ (NSString*)logLevelToString:(int)level;
+ (NSString *)logLevelToString:(int)level;
/**
* Returns the session history size.
@ -346,13 +398,23 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (void)setSessionHistorySize:(int)sessionHistorySize;
/**
* Returns the session specified with <code>sessionId</code> from the session history.
* Returns the session specified with <code>sessionId</code> from the session
* history.
*
* @param sessionId session identifier
* @return session specified with sessionId or nil if it is not found in the history
* @return session specified with sessionId or nil if it is not found in the
* history
*/
+ (id<Session>)getSession:(long)sessionId;
/**
* Deletes the session specified with <code>sessionId</code> from the session
* history.
*
* @param sessionId session identifier
*/
+ (void)deleteSession:(long)sessionId;
/**
* Returns the last session created from the session history.
*
@ -363,8 +425,8 @@ typedef NS_ENUM(NSUInteger, Signal) {
/**
* Returns the last session completed from the session history.
*
* @return the last session completed. If there are no completed sessions in the history this
* method will return nil
* @return the last session completed. If there are no completed sessions in the
* history this method will return nil
*/
+ (id<Session>)getLastCompletedSession;
@ -373,7 +435,7 @@ typedef NS_ENUM(NSUInteger, Signal) {
*
* @return all sessions in the session history
*/
+ (NSArray*)getSessions;
+ (NSArray *)getSessions;
/**
* <p>Clears all, including ongoing, sessions in the session history.
@ -386,28 +448,28 @@ typedef NS_ENUM(NSUInteger, Signal) {
*
* @return all FFmpeg sessions in the session history
*/
+ (NSArray*)getFFmpegSessions;
+ (NSArray *)getFFmpegSessions;
/**
* <p>Returns all FFprobe sessions in the session history.
*
* @return all FFprobe sessions in the session history
*/
+ (NSArray*)getFFprobeSessions;
+ (NSArray *)getFFprobeSessions;
/**
* <p>Returns all MediaInformation sessions in the session history.
*
* @return all MediaInformation sessions in the session history
*/
+ (NSArray*)getMediaInformationSessions;
+ (NSArray *)getMediaInformationSessions;
/**
* <p>Returns sessions that have the given state.
*
* @return sessions that have the given state from the session history
*/
+ (NSArray*)getSessionsByState:(SessionState)state;
+ (NSArray *)getSessionsByState:(SessionState)state;
/**
* Returns the active log redirection strategy.
@ -421,14 +483,16 @@ typedef NS_ENUM(NSUInteger, Signal) {
*
* @param logRedirectionStrategy log redirection strategy
*/
+ (void)setLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
+ (void)setLogRedirectionStrategy:
(LogRedirectionStrategy)logRedirectionStrategy;
/**
* <p>Returns the number of async messages that are not transmitted to the callbacks for
* this session.
* <p>Returns the number of async messages that are not transmitted to the
* callbacks for this session.
*
* @param sessionId id of the session
* @return number of async messages that are not transmitted to the callbacks for this session
* @return number of async messages that are not transmitted to the callbacks
* for this session
*/
+ (int)messagesInTransmit:(long)sessionId;
@ -438,24 +502,25 @@ typedef NS_ENUM(NSUInteger, Signal) {
* @param state session state
* @return string value
*/
+ (NSString*)sessionStateToString:(SessionState)state;
+ (NSString *)sessionStateToString:(SessionState)state;
/**
* <p>Parses the given command into arguments. Uses space character to split the arguments.
* Supports single and double quote characters.
* <p>Parses the given command into arguments. Uses space character to split the
* arguments. Supports single and double quote characters.
*
* @param command string command
* @return array of arguments
*/
+ (NSArray*)parseArguments:(NSString*)command;
+ (NSArray *)parseArguments:(NSString *)command;
/**
* <p>Concatenates arguments into a string adding a space character between two arguments.
* <p>Concatenates arguments into a string adding a space character between two
* arguments.
*
* @param arguments arguments
* @return concatenated string containing all arguments
*/
+ (NSString*)argumentsToString:(NSArray*)arguments;
+ (NSString *)argumentsToString:(NSArray *)arguments;
@end

File diff suppressed because it is too large Load Diff

@ -20,10 +20,10 @@
#ifndef FFMPEG_KIT_FFMPEG_SESSION_H
#define FFMPEG_KIT_FFMPEG_SESSION_H
#import <Foundation/Foundation.h>
#import "AbstractSession.h"
#import "StatisticsCallback.h"
#import "FFmpegSessionCompleteCallback.h"
#import "StatisticsCallback.h"
#import <Foundation/Foundation.h>
/**
* <p>An FFmpeg session.
@ -36,7 +36,7 @@
* @param arguments command arguments
* @return created session
*/
+ (instancetype)create:(NSArray*)arguments;
+ (instancetype)create:(NSArray *)arguments;
/**
* Builds a new FFmpeg session.
@ -45,7 +45,8 @@
* @param completeCallback session specific complete callback
* @return created session
*/
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback;
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback;
/**
* Builds a new FFmpeg session.
@ -56,7 +57,10 @@
* @param statisticsCallback session specific statistics callback
* @return created session
*/
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback;
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* Builds a new FFmpeg session.
@ -68,7 +72,11 @@
* @param logRedirectionStrategy session specific log redirection strategy
* @return created session
*/
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
/**
* Returns the session specific statistics callback.
@ -85,47 +93,49 @@
- (FFmpegSessionCompleteCallback)getCompleteCallback;
/**
* Returns all statistics entries generated for this session. If there are asynchronous
* messages that are not delivered yet, this method waits for them until the given timeout.
* Returns all statistics entries generated for this session. If there are
* asynchronous messages that are not delivered yet, this method waits for them
* until the given timeout.
*
* @param waitTimeout wait timeout for asynchronous messages in milliseconds
* @return list of statistics entries generated for this session
*/
- (NSArray*)getAllStatisticsWithTimeout:(int)waitTimeout;
- (NSArray *)getAllStatisticsWithTimeout:(int)waitTimeout;
/**
* Returns all statistics entries generated for this session. If there are asynchronous
* messages that are not delivered yet, this method waits for them until
* AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit expires.
* Returns all statistics entries generated for this session. If there are
* asynchronous messages that are not delivered yet, this method waits for them
* until AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit expires.
*
* @return list of statistics entries generated for this session
*/
- (NSArray*)getAllStatistics;
- (NSArray *)getAllStatistics;
/**
* Returns all statistics entries delivered for this session. Note that if there are
* asynchronous messages that are not delivered yet, this method will not wait for
* them and will return immediately.
* Returns all statistics entries delivered for this session. Note that if there
* are asynchronous messages that are not delivered yet, this method will not
* wait for them and will return immediately.
*
* @return list of statistics entries received for this session
*/
- (NSArray*)getStatistics;
- (NSArray *)getStatistics;
/**
* Returns the last received statistics entry.
*
* @return the last received statistics entry or nil if there are not any statistics entries
* received
* @return the last received statistics entry or nil if there are not any
* statistics entries received
*/
- (Statistics*)getLastReceivedStatistics;
- (Statistics *)getLastReceivedStatistics;
/**
* Adds a new statistics entry for this session. It is invoked internally by <code>FFmpegKit</code> library methods.
* Must not be used by user applications.
* Adds a new statistics entry for this session. It is invoked internally by
* <code>FFmpegKit</code> library methods. Must not be used by user
* applications.
*
* @param statistics statistics entry
*/
- (void)addStatistics:(Statistics*)statistics;
- (void)addStatistics:(Statistics *)statistics;
@end

@ -25,33 +25,63 @@
@implementation FFmpegSession {
StatisticsCallback _statisticsCallback;
FFmpegSessionCompleteCallback _completeCallback;
NSMutableArray* _statistics;
NSRecursiveLock* _statisticsLock;
NSMutableArray *_statistics;
NSRecursiveLock *_statisticsLock;
}
+ (void)initialize {
// EMPTY INITIALIZE
}
+ (instancetype)create:(NSArray*)arguments {
return [[self alloc] init:arguments withCompleteCallback:nil withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback withLogRedirectionStrategy:logRedirectionStrategy];
}
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
self = [super init:arguments withLogCallback:logCallback withLogRedirectionStrategy:logRedirectionStrategy];
+ (instancetype)create:(NSArray *)arguments {
return [[self alloc] init:arguments
withCompleteCallback:nil
withLogCallback:nil
withStatisticsCallback:nil
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:nil
withStatisticsCallback:nil
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback
withLogRedirectionStrategy:logRedirectionStrategy];
}
- (instancetype)init:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
self = [super init:arguments
withLogCallback:logCallback
withLogRedirectionStrategy:logRedirectionStrategy];
if (self) {
_statisticsCallback = statisticsCallback;
@ -71,30 +101,36 @@
return _completeCallback;
}
- (NSArray*)getAllStatisticsWithTimeout:(int)waitTimeout {
- (NSArray *)getAllStatisticsWithTimeout:(int)waitTimeout {
[self waitForAsynchronousMessagesInTransmit:waitTimeout];
if ([self thereAreAsynchronousMessagesInTransmit]) {
NSLog(@"getAllStatisticsWithTimeout was called to return all statistics but there are still statistics being transmitted for session id %ld.", [self getSessionId]);
NSLog(
@"getAllStatisticsWithTimeout was called to return all statistics "
@"but "
@"there are still statistics being transmitted for session id %ld.",
[self getSessionId]);
}
return [self getStatistics];
}
- (NSArray*)getAllStatistics {
return [self getAllStatisticsWithTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
- (NSArray *)getAllStatistics {
return [self
getAllStatisticsWithTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
}
- (NSArray*)getStatistics {
- (NSArray *)getStatistics {
[_statisticsLock lock];
NSArray* statisticsCopy = [_statistics copy];
NSArray *statisticsCopy = [_statistics copy];
[_statisticsLock unlock];
return statisticsCopy;
}
- (Statistics*)getLastReceivedStatistics {
Statistics* lastStatistics = nil;
- (Statistics *)getLastReceivedStatistics {
Statistics *lastStatistics = nil;
[_statisticsLock lock];
if ([_statistics count] > 0) {
@ -105,7 +141,7 @@
return lastStatistics;
}
- (void)addStatistics:(Statistics*)statistics {
- (void)addStatistics:(Statistics *)statistics {
[_statisticsLock lock];
[_statistics addObject:statistics];
[_statisticsLock unlock];
@ -124,4 +160,3 @@
}
@end

@ -23,13 +23,12 @@
@class FFmpegSession;
/**
* <p>Callback function that is invoked when an asynchronous <code>FFmpeg</code> session has ended.
* <p>Session has either SessionStateCompleted or SessionStateFailed state when
* the callback is invoked.
* <p>If it has SessionStateCompleted state, <code>ReturnCode</code> should be checked to
* see the execution result.
* <p>If <code>getState</code> returns SessionStateFailed then
* <code>getFailStackTrace</code> should be used to get the failure reason.
* <p>Callback function that is invoked when an asynchronous <code>FFmpeg</code>
* session has ended. <p>Session has either SessionStateCompleted or
* SessionStateFailed state when the callback is invoked. <p>If it has
* SessionStateCompleted state, <code>ReturnCode</code> should be checked to see
* the execution result. <p>If <code>getState</code> returns SessionStateFailed
* then <code>getFailStackTrace</code> should be used to get the failure reason.
* <pre>
* switch ([session getState]) {
* case SessionStateCompleted:
@ -43,7 +42,7 @@
*
* @param session session of the completed execution
*/
typedef void (^FFmpegSessionCompleteCallback)(FFmpegSession* session);
typedef void (^FFmpegSessionCompleteCallback)(FFmpegSession *session);
#import "FFmpegSession.h"

@ -20,27 +20,31 @@
#ifndef FFPROBE_KIT_H
#define FFPROBE_KIT_H
#import <string.h>
#import <stdlib.h>
#import <Foundation/Foundation.h>
#import "FFprobeSession.h"
#import "MediaInformationJsonParser.h"
#import <Foundation/Foundation.h>
#import <stdlib.h>
#import <string.h>
/**
* <p>Main class to run <code>FFprobe</code> commands. Supports executing commands both synchronously and
* asynchronously.
* <pre>
* FFprobeSession *session = [FFprobeKit execute:@"-hide_banner -v error -show_entries format=size -of default=noprint_wrappers=1 file1.mp4"];
*
* FFprobeSession *asyncSession = [FFprobeKit executeAsync:@"-hide_banner -v error -show_entries format=size -of default=noprint_wrappers=1 file1.mp4" withCompleteCallback:completeCallback];
* <p>Main class to run <code>FFprobe</code> commands. Supports executing
* commands both synchronously and asynchronously. <pre> FFprobeSession *session
* = [FFprobeKit execute:@"-hide_banner -v error -show_entries format=size -of
* default=noprint_wrappers=1 file1.mp4"];
*
* FFprobeSession *asyncSession = [FFprobeKit executeAsync:@"-hide_banner -v
* error -show_entries format=size -of default=noprint_wrappers=1 file1.mp4"
* withCompleteCallback:completeCallback];
* </pre>
* <p>Provides overloaded <code>execute</code> methods to define session specific callbacks.
* <pre>
* FFprobeSession *session = [FFprobeKit executeAsync:@"-hide_banner -v error -show_entries format=size -of default=noprint_wrappers=1 file1.mp4" withCompleteCallback:completeCallback withLogCallback:logCallback];
* <p>Provides overloaded <code>execute</code> methods to define session
* specific callbacks. <pre> FFprobeSession *session = [FFprobeKit
* executeAsync:@"-hide_banner -v error -show_entries format=size -of
* default=noprint_wrappers=1 file1.mp4" withCompleteCallback:completeCallback
* withLogCallback:logCallback];
* </pre>
* <p>It can extract media information for a file or a url, using getMediaInformation method.
* <pre>
* MediaInformationSession *session = [FFprobeKit getMediaInformation:@"file1.mp4"];
* <p>It can extract media information for a file or a url, using
* getMediaInformation method. <pre> MediaInformationSession *session =
* [FFprobeKit getMediaInformation:@"file1.mp4"];
* </pre>
*/
@interface FFprobeKit : NSObject
@ -51,125 +55,173 @@
* @param arguments FFprobe command options/arguments as string array
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArguments:(NSArray*)arguments;
+ (FFprobeSession *)executeWithArguments:(NSArray *)arguments;
/**
* <p>Starts an asynchronous FFprobe execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param arguments FFprobe command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution has
* completed
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback;
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFprobe execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param arguments FFprobe command options/arguments as string array
* @param completeCallback callback that will be notified when execution has completed
* @param completeCallback callback that will be notified when execution has
* completed
* @param logCallback callback that will receive logs
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback;
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback;
/**
* <p>Starts an asynchronous FFprobe execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param arguments FFprobe command options/arguments as string array
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param completeCallback callback that will be called when the execution has
* completed
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution with arguments provided.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param arguments FFprobe command options/arguments as string array
* @param completeCallback callback that will be notified when execution has completed
* @param completeCallback callback that will be notified when execution has
* completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Synchronously executes FFprobe command provided. Space character is used to split command
* into arguments. You can use single or double quote characters to specify arguments inside
* your command.
* <p>Synchronously executes FFprobe command provided. Space character is used
* to split command into arguments. You can use single or double quote
* characters to specify arguments inside your command.
*
* @param command FFprobe command
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)execute:(NSString*)command;
+ (FFprobeSession *)execute:(NSString *)command;
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
* <p>Starts an asynchronous FFprobe execution for the given command. Space
* character is used to split the command into arguments. You can use single or
* double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param command FFprobe command
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution has
* completed
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback;
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback;
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
* <p>Starts an asynchronous FFprobe execution for the given command. Space
* character is used to split the command into arguments. You can use single or
* double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param command FFprobe command
* @param completeCallback callback that will be notified when execution has completed
* @param completeCallback callback that will be notified when execution has
* completed
* @param logCallback callback that will receive logs
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback;
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback;
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
* <p>Starts an asynchronous FFprobe execution for the given command. Space
* character is used to split the command into arguments. You can use single or
* double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param command FFprobe command
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param completeCallback callback that will be called when the execution has
* completed
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
* <p>Starts an asynchronous FFprobe execution for the given command. Space
* character is used to split the command into arguments. You can use single or
* double quote characters to specify arguments inside your command.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param command FFprobe command
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution has
* completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return FFprobe session created for this execution
*/
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Extracts media information for the file specified with path.
@ -177,7 +229,7 @@
* @param path path or uri of a media file
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformation:(NSString*)path;
+ (MediaInformationSession *)getMediaInformation:(NSString *)path;
/**
* <p>Extracts media information for the file specified with path.
@ -186,116 +238,177 @@
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformation:(NSString*)path withTimeout:(int)waitTimeout;
+ (MediaInformationSession *)getMediaInformation:(NSString *)path
withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
* <p>Starts an asynchronous FFprobe execution to extract the media information
* for the specified file.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param path path or uri of a media file
* @param completeCallback callback that will be called when the execution has completed
* @param completeCallback callback that will be called when the execution has
* completed
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback;
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback;
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
* <p>Starts an asynchronous FFprobe execution to extract the media information
* for the specified file.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param path path or uri of a media file
* @param completeCallback callback that will be notified when execution has completed
* @param completeCallback callback that will be notified when execution has
* completed
* @param logCallback callback that will receive logs
* @param waitTimeout max time to wait until media information is transmitted
* @param waitTimeout max time to wait until media information is
* transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withTimeout:(int)waitTimeout;
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
* <p>Starts an asynchronous FFprobe execution to extract the media information
* for the specified file.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param path path or uri of a media file
* @param completeCallback callback that will be called when the execution has completed
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param completeCallback callback that will be called when the execution has
* completed
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue;
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
* <p>Starts an asynchronous FFprobe execution to extract the media information
* for the specified file.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param path path or uri of a media file
* @param completeCallback callback that will be notified when execution has completed
* @param completeCallback callback that will be notified when execution has
* completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @param waitTimeout max time to wait until media information is
* transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout;
/**
* <p>Extracts media information using the command provided asynchronously.
*
* @param command FFprobe command that prints media information for a file in JSON format
* @param command FFprobe command that prints media information for a file in
* JSON format
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationFromCommand:(NSString*)command;
+ (MediaInformationSession *)getMediaInformationFromCommand:(NSString *)command;
/**
* <p>Starts an asynchronous FFprobe execution to extract media information using a command. The command passed to
* this method must generate the output in JSON format in order to successfully extract media information from it.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param command FFprobe command that prints media information for a file in JSON format
* @param completeCallback callback that will be notified when execution has completed
* <p>Starts an asynchronous FFprobe execution to extract media information
* using a command. The command passed to this method must generate the output
* in JSON format in order to successfully extract media information from it.
*
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param command FFprobe command that prints media information for a
* file in JSON format
* @param completeCallback callback that will be notified when execution has
* completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @param waitTimeout max time to wait until media information is
* transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationFromCommandAsync:(NSString*)command withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
+ (MediaInformationSession *)
getMediaInformationFromCommandAsync:(NSString *)command
withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution to extract media information using command arguments. The command
* passed to this method must generate the output in JSON format in order to successfully extract media information
* from it.
*
* <p>Note that this method returns immediately and does not wait the execution to complete. You must use an
* MediaInformationSessionCompleteCallback if you want to be notified about the result.
*
* @param arguments FFprobe command that prints media information for a file in JSON format
* @param completeCallback callback that will be notified when execution has completed
* <p>Starts an asynchronous FFprobe execution to extract media information
* using command arguments. The command passed to this method must generate the
* output in JSON format in order to successfully extract media information from
* it.
*
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param arguments FFprobe command that prints media information for a
* file in JSON format
* @param completeCallback callback that will be notified when execution has
* completed
* @param logCallback callback that will receive logs
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @param waitTimeout max time to wait until media information is
* transmitted
* @return media information session created for this execution
*/
+ (MediaInformationSession*)getMediaInformationFromCommandArgumentsAsync:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
+ (MediaInformationSession *)
getMediaInformationFromCommandArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout;
/**
* <p>Lists all FFprobe sessions in the session history.
*
* @return all FFprobe sessions in the session history
*/
+ (NSArray*)listFFprobeSessions;
+ (NSArray *)listFFprobeSessions;
/**
* <p>Lists all MediaInformation sessions in the session history.
*
* @return all MediaInformation sessions in the session history
*/
+ (NSArray*)listMediaInformationSessions;
+ (NSArray *)listMediaInformationSessions;
@end

@ -28,135 +28,257 @@
[FFmpegKitConfig class];
}
+ (NSArray*)defaultGetMediaInformationCommandArguments:(NSString*)path {
return [[NSArray alloc] initWithObjects:@"-v", @"error", @"-hide_banner", @"-print_format", @"json", @"-show_format", @"-show_streams", @"-show_chapters", @"-i", path, nil];
+ (NSArray *)defaultGetMediaInformationCommandArguments:(NSString *)path {
return [[NSArray alloc]
initWithObjects:@"-v", @"error", @"-hide_banner", @"-print_format",
@"json", @"-show_format", @"-show_streams",
@"-show_chapters", @"-i", path, nil];
}
+ (FFprobeSession*)executeWithArguments:(NSArray*)arguments {
FFprobeSession* session = [FFprobeSession create:arguments];
+ (FFprobeSession *)executeWithArguments:(NSArray *)arguments {
FFprobeSession *session = [FFprobeSession create:arguments];
[FFmpegKitConfig ffprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback];
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback {
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback];
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback];
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session;
}
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback];
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session;
}
+ (FFprobeSession*)execute:(NSString*)command {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command]];
+ (FFprobeSession *)execute:(NSString *)command {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig ffprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback];
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback];
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback];
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session;
}
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback];
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session;
}
+ (MediaInformationSession*)getMediaInformation:(NSString*)path {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments];
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
+ (MediaInformationSession *)getMediaInformation:(NSString *)path {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments];
[FFmpegKitConfig
getMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session;
}
+ (MediaInformationSession*)getMediaInformation:(NSString*)path withTimeout:(int)waitTimeout {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments];
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:waitTimeout];
+ (MediaInformationSession *)getMediaInformation:(NSString *)path
withTimeout:(int)waitTimeout {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments];
[FFmpegKitConfig getMediaInformationExecute:session
withTimeout:waitTimeout];
return session;
}
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig
asyncGetMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session;
}
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withTimeout:(int)waitTimeout {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session withTimeout:waitTimeout];
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
withTimeout:(int)waitTimeout {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
withTimeout:waitTimeout];
return session;
}
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig
asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session;
}
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout];
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session;
}
+ (MediaInformationSession*)getMediaInformationFromCommand:(NSString*)command {
MediaInformationSession* session = [MediaInformationSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
+ (MediaInformationSession *)getMediaInformationFromCommand:
(NSString *)command {
MediaInformationSession *session = [MediaInformationSession
create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig
getMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session;
}
+ (MediaInformationSession*)getMediaInformationFromCommandAsync:(NSString*)command withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout {
MediaInformationSession* session = [MediaInformationSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout];
+ (MediaInformationSession *)
getMediaInformationFromCommandAsync:(NSString *)command
withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
MediaInformationSession *session =
[MediaInformationSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session;
}
+ (MediaInformationSession*)getMediaInformationFromCommandArgumentsAsync:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout {
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout];
+ (MediaInformationSession *)
getMediaInformationFromCommandArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session;
}
+ (NSArray*)listFFprobeSessions {
+ (NSArray *)listFFprobeSessions {
return [FFmpegKitConfig getFFprobeSessions];
}
+ (NSArray*)listMediaInformationSessions {
+ (NSArray *)listMediaInformationSessions {
return [FFmpegKitConfig getMediaInformationSessions];
}

@ -20,9 +20,9 @@
#ifndef FFMPEG_KIT_FFPROBE_SESSION_H
#define FFMPEG_KIT_FFPROBE_SESSION_H
#import <Foundation/Foundation.h>
#import "AbstractSession.h"
#import "FFprobeSessionCompleteCallback.h"
#import <Foundation/Foundation.h>
/**
* <p>An FFprobe session.
@ -35,7 +35,7 @@
* @param arguments command arguments
* @return created session
*/
+ (instancetype)create:(NSArray*)arguments;
+ (instancetype)create:(NSArray *)arguments;
/**
* Builds a new FFprobe session.
@ -44,7 +44,8 @@
* @param completeCallback session specific complete callback
* @return created session
*/
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback;
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback;
/**
* Builds a new FFprobe session.
@ -54,7 +55,9 @@
* @param logCallback session specific log callback
* @return created session
*/
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback;
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback;
/**
* Builds a new FFprobe session.
@ -65,7 +68,10 @@
* @param logRedirectionStrategy session specific log redirection strategy
* @return created session
*/
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
/**
* Returns the session specific complete callback.

@ -29,25 +29,48 @@
// EMPTY INITIALIZE
}
+ (instancetype)create:(NSArray*)arguments {
return [[self alloc] init:arguments withCompleteCallback:nil withLogCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
+ (instancetype)create:(NSArray *)arguments {
return [[self alloc] init:arguments
withCompleteCallback:nil
withLogCallback:nil
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:nil
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withLogRedirectionStrategy:logRedirectionStrategy];
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withLogRedirectionStrategy:logRedirectionStrategy];
}
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
- (instancetype)init:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
self = [super init:arguments withLogCallback:logCallback withLogRedirectionStrategy:logRedirectionStrategy];
self = [super init:arguments
withLogCallback:logCallback
withLogRedirectionStrategy:logRedirectionStrategy];
if (self) {
_completeCallback = completeCallback;
@ -73,4 +96,3 @@
}
@end

@ -23,17 +23,14 @@
@class FFprobeSession;
/**
* <p>Callback function that is invoked when an asynchronous <code>FFprobe</code> session has ended.
* <p>Session has either SessionStateCompleted or SessionStateFailed state when
* the callback is invoked.
* <p>If it has SessionStateCompleted state, <code>ReturnCode</code> should be checked to
* see the execution result.
* <p>If <code>getState</code> returns SessionStateFailed then
* <code>getFailStackTrace</code> should be used to get the failure reason.
* <pre>
* switch ([session getState]) {
* case SessionStateCompleted:
* ReturnCode *returnCode = [session getReturnCode];
* <p>Callback function that is invoked when an asynchronous
* <code>FFprobe</code> session has ended. <p>Session has either
* SessionStateCompleted or SessionStateFailed state when the callback is
* invoked. <p>If it has SessionStateCompleted state, <code>ReturnCode</code>
* should be checked to see the execution result. <p>If <code>getState</code>
* returns SessionStateFailed then <code>getFailStackTrace</code> should be used
* to get the failure reason. <pre> switch ([session getState]) { case
* SessionStateCompleted: ReturnCode *returnCode = [session getReturnCode];
* break;
* case SessionStateFailed:
* NSString *failStackTrace = [session getFailStackTrace];
@ -43,7 +40,7 @@
*
* @param session session of the completed execution
*/
typedef void (^FFprobeSessionCompleteCallback)(FFprobeSession* session);
typedef void (^FFprobeSessionCompleteCallback)(FFprobeSession *session);
#import "FFprobeSession.h"

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021 Taner Sener
* Copyright (c) 2024 ARTHENICA LTD
*
* This file is part of FFmpegKit.
*
@ -23,11 +24,12 @@
/**
* <p>Enumeration type for log levels.
*/
typedef NS_ENUM(NSUInteger, Level) {
typedef NS_ENUM(NSInteger, Level) {
/**
* This log level is defined by FFmpegKit. It is used to specify logs printed to stderr by
* FFmpeg. Logs that has this level are not filtered and always redirected.
* This log level is defined by FFmpegKit. It is used to specify logs
* printed to stderr by FFmpeg. Logs that has this level are not filtered
* and always redirected.
*/
LevelAVLogStdErr = -16,
@ -74,7 +76,7 @@ typedef NS_ENUM(NSUInteger, Level) {
* Stuff which is only useful for libav* developers.
*/
LevelAVLogDebug = 48,
/**
* Extremely verbose debugging, useful for libav* development.
*/

@ -27,13 +27,13 @@
*/
@interface Log : NSObject
- (instancetype)init:(long)sessionId :(int)level :(NSString*)message;
- (instancetype)init:(long)sessionId :(int)level :(NSString *)message;
- (long)getSessionId;
- (int)getLevel;
- (NSString*)getMessage;
- (NSString *)getMessage;
@end

@ -25,7 +25,7 @@
NSString *_message;
}
- (instancetype)init:(long)sessionId :(int)level :(NSString*)message {
- (instancetype)init:(long)sessionId :(int)level :(NSString *)message {
self = [super init];
if (self) {
_sessionId = sessionId;
@ -44,7 +44,7 @@
return _level;
}
- (NSString*)getMessage {
- (NSString *)getMessage {
return _message;
}

@ -20,14 +20,14 @@
#ifndef FFMPEG_KIT_LOG_CALLBACK_H
#define FFMPEG_KIT_LOG_CALLBACK_H
#import <Foundation/Foundation.h>
#import "Log.h"
#import <Foundation/Foundation.h>
/**
* <p>Callback that receives logs generated for <code>FFmpegKit</code> sessions.
*
* @param log log entry
*/
typedef void (^LogCallback)(Log* log);
typedef void (^LogCallback)(Log *log);
#endif // FFMPEG_KIT_LOG_CALLBACK_H

@ -20,10 +20,13 @@ libffmpegkit_la_SOURCES = \
ReturnCode.m \
Statistics.m \
StreamInformation.m \
ffmpeg_context.c \
ffmpegkit_exception.m \
fftools_cmdutils.c \
fftools_ffmpeg.c \
fftools_ffmpeg_dec.c \
fftools_ffmpeg_demux.c \
fftools_ffmpeg_enc.c \
fftools_ffmpeg_filter.c \
fftools_ffmpeg_hw.c \
fftools_ffmpeg_mux.c \
@ -62,6 +65,7 @@ include_HEADERS = \
Statistics.h \
StatisticsCallback.h \
StreamInformation.h \
ffmpeg_context.h \
ffmpegkit_exception.h \
fftools_cmdutils.h \
fftools_ffmpeg.h \

File diff suppressed because it is too large Load Diff

@ -20,152 +20,155 @@
#ifndef FFMPEG_KIT_MEDIA_INFORMATION_H
#define FFMPEG_KIT_MEDIA_INFORMATION_H
#import <Foundation/Foundation.h>
#import "Chapter.h"
#import "StreamInformation.h"
#import <Foundation/Foundation.h>
extern NSString* const MediaKeyMediaProperties;
extern NSString* const MediaKeyFilename;
extern NSString* const MediaKeyFormat;
extern NSString* const MediaKeyFormatLong;
extern NSString* const MediaKeyStartTime;
extern NSString* const MediaKeyDuration;
extern NSString* const MediaKeySize;
extern NSString* const MediaKeyBitRate;
extern NSString* const MediaKeyTags;
extern NSString *const MediaKeyMediaProperties;
extern NSString *const MediaKeyFilename;
extern NSString *const MediaKeyFormat;
extern NSString *const MediaKeyFormatLong;
extern NSString *const MediaKeyStartTime;
extern NSString *const MediaKeyDuration;
extern NSString *const MediaKeySize;
extern NSString *const MediaKeyBitRate;
extern NSString *const MediaKeyTags;
/**
* Media information class.
*/
@interface MediaInformation : NSObject
- (instancetype)init:(NSDictionary*)mediaDictionary withStreams:(NSArray*)streams withChapters:(NSArray*)chapters;
- (instancetype)init:(NSDictionary *)mediaDictionary
withStreams:(NSArray *)streams
withChapters:(NSArray *)chapters;
/**
* Returns file name.
*
* @return media file name
*/
- (NSString*)getFilename;
- (NSString *)getFilename;
/**
* Returns format.
*
* @return media format
*/
- (NSString*)getFormat;
- (NSString *)getFormat;
/**
* Returns long format.
*
* @return media long format
*/
- (NSString*)getLongFormat;
- (NSString *)getLongFormat;
/**
* Returns duration.
*
* @return media duration in "seconds.microseconds" format
*/
- (NSString*)getDuration;
- (NSString *)getDuration;
/**
* Returns start time.
*
* @return media start time in milliseconds
*/
- (NSString*)getStartTime;
- (NSString *)getStartTime;
/**
* Returns size.
*
* @return media size in bytes
*/
- (NSString*)getSize;
- (NSString *)getSize;
/**
* Returns bitrate.
*
* @return media bitrate in kb/s
*/
- (NSString*)getBitrate;
- (NSString *)getBitrate;
/**
* Returns all tags.
*
* @return tags dictionary
*/
- (NSDictionary*)getTags;
- (NSDictionary *)getTags;
/**
* Returns all streams.
*
* @return streams array
*/
- (NSArray*)getStreams;
- (NSArray *)getStreams;
/**
* Returns all chapters.
*
* @return chapters array
*/
- (NSArray*)getChapters;
- (NSArray *)getChapters;
/**
* Returns the property associated with the key.
*
* @return property as string or nil if the key is not found
*/
- (NSString*)getStringProperty:(NSString*)key;
- (NSString *)getStringProperty:(NSString *)key;
/**
* Returns the property associated with the key.
*
* @return property as number or nil if the key is not found
*/
- (NSNumber*)getNumberProperty:(NSString*)key;
- (NSNumber *)getNumberProperty:(NSString *)key;
/**
* Returns the property associated with the key.
*
* @return property as id or nil if the key is not found
*/
- (id)getProperty:(NSString*)key;
*/
- (id)getProperty:(NSString *)key;
/**
* Returns the format property associated with the key.
*
* @return format property as string or nil if the key is not found
*/
- (NSString*)getStringFormatProperty:(NSString*)key;
- (NSString *)getStringFormatProperty:(NSString *)key;
/**
* Returns the format property associated with the key.
*
* @return format property as number or nil if the key is not found
*/
- (NSNumber*)getNumberFormatProperty:(NSString*)key;
- (NSNumber *)getNumberFormatProperty:(NSString *)key;
/**
* Returns the format property associated with the key.
*
* @return format property as id or nil if the key is not found
*/
- (id)getFormatProperty:(NSString*)key;
*/
- (id)getFormatProperty:(NSString *)key;
/**
* Returns all format properties defined.
*
* @return all format properties in a dictionary or nil if no format properties are defined
*/
- (NSDictionary*)getFormatProperties;
* @return all format properties in a dictionary or nil if no format properties
* are defined
*/
- (NSDictionary *)getFormatProperties;
/**
* Returns all properties defined.
*
* @return all properties in a dictionary or nil if no properties are defined
*/
- (NSDictionary*)getAllProperties;
*/
- (NSDictionary *)getAllProperties;
@end

@ -19,15 +19,15 @@
#import "MediaInformation.h"
NSString* const MediaKeyFormatProperties = @"format";
NSString* const MediaKeyFilename = @"filename";
NSString* const MediaKeyFormat = @"format_name";
NSString* const MediaKeyFormatLong = @"format_long_name";
NSString* const MediaKeyStartTime = @"start_time";
NSString* const MediaKeyDuration = @"duration";
NSString* const MediaKeySize = @"size";
NSString* const MediaKeyBitRate = @"bit_rate";
NSString* const MediaKeyTags = @"tags";
NSString *const MediaKeyFormatProperties = @"format";
NSString *const MediaKeyFilename = @"filename";
NSString *const MediaKeyFormat = @"format_name";
NSString *const MediaKeyFormatLong = @"format_long_name";
NSString *const MediaKeyStartTime = @"start_time";
NSString *const MediaKeyDuration = @"duration";
NSString *const MediaKeySize = @"size";
NSString *const MediaKeyBitRate = @"bit_rate";
NSString *const MediaKeyTags = @"tags";
@implementation MediaInformation {
@ -45,10 +45,11 @@ NSString* const MediaKeyTags = @"tags";
* Stores chapters.
*/
NSArray *chapterArray;
}
- (instancetype)init:(NSDictionary*)mediaDictionary withStreams:(NSArray*)streams withChapters:(NSArray*)chapters{
- (instancetype)init:(NSDictionary *)mediaDictionary
withStreams:(NSArray *)streams
withChapters:(NSArray *)chapters {
self = [super init];
if (self) {
dictionary = mediaDictionary;
@ -59,48 +60,48 @@ NSString* const MediaKeyTags = @"tags";
return self;
}
- (NSString*)getFilename {
- (NSString *)getFilename {
return [self getStringFormatProperty:MediaKeyFilename];
}
- (NSString*)getFormat {
- (NSString *)getFormat {
return [self getStringFormatProperty:MediaKeyFormat];
}
- (NSString*)getLongFormat {
- (NSString *)getLongFormat {
return [self getStringFormatProperty:MediaKeyFormatLong];
}
- (NSString*)getStartTime {
- (NSString *)getStartTime {
return [self getStringFormatProperty:MediaKeyStartTime];
}
- (NSString*)getDuration {
- (NSString *)getDuration {
return [self getStringFormatProperty:MediaKeyDuration];
}
- (NSString*)getSize {
- (NSString *)getSize {
return [self getStringFormatProperty:MediaKeySize];
}
- (NSString*)getBitrate {
- (NSString *)getBitrate {
return [self getStringFormatProperty:MediaKeyBitRate];
}
- (NSDictionary*)getTags {
- (NSDictionary *)getTags {
return [self getFormatProperty:MediaKeyTags];
}
- (NSArray*)getStreams {
- (NSArray *)getStreams {
return streamArray;
}
- (NSArray*)getChapters {
- (NSArray *)getChapters {
return chapterArray;
}
- (NSString*)getStringProperty:(NSString*)key {
NSDictionary* allProperties = [self getAllProperties];
- (NSString *)getStringProperty:(NSString *)key {
NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) {
return nil;
}
@ -108,8 +109,8 @@ NSString* const MediaKeyTags = @"tags";
return allProperties[key];
}
- (NSNumber*)getNumberProperty:(NSString*)key {
NSDictionary* allProperties = [self getAllProperties];
- (NSNumber *)getNumberProperty:(NSString *)key {
NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) {
return nil;
}
@ -117,8 +118,8 @@ NSString* const MediaKeyTags = @"tags";
return allProperties[key];
}
- (id)getProperty:(NSString*)key {
NSDictionary* allProperties = [self getAllProperties];
- (id)getProperty:(NSString *)key {
NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) {
return nil;
}
@ -126,8 +127,8 @@ NSString* const MediaKeyTags = @"tags";
return allProperties[key];
}
- (NSString*)getStringFormatProperty:(NSString*)key {
NSDictionary* formatProperties = [self getFormatProperties];
- (NSString *)getStringFormatProperty:(NSString *)key {
NSDictionary *formatProperties = [self getFormatProperties];
if (formatProperties == nil) {
return nil;
}
@ -135,8 +136,8 @@ NSString* const MediaKeyTags = @"tags";
return formatProperties[key];
}
- (NSNumber*)getNumberFormatProperty:(NSString*)key {
NSDictionary* formatProperties = [self getFormatProperties];
- (NSNumber *)getNumberFormatProperty:(NSString *)key {
NSDictionary *formatProperties = [self getFormatProperties];
if (formatProperties == nil) {
return nil;
}
@ -144,8 +145,8 @@ NSString* const MediaKeyTags = @"tags";
return formatProperties[key];
}
- (id)getFormatProperty:(NSString*)key {
NSDictionary* formatProperties = [self getFormatProperties];
- (id)getFormatProperty:(NSString *)key {
NSDictionary *formatProperties = [self getFormatProperties];
if (formatProperties == nil) {
return nil;
}
@ -153,11 +154,11 @@ NSString* const MediaKeyTags = @"tags";
return formatProperties[key];
}
- (NSDictionary*)getFormatProperties {
- (NSDictionary *)getFormatProperties {
return dictionary[MediaKeyFormatProperties];
}
- (NSDictionary*)getAllProperties {
- (NSDictionary *)getAllProperties {
return dictionary;
}

@ -20,8 +20,8 @@
#ifndef FFMPEG_KIT_MEDIA_INFORMATION_PARSER_H
#define FFMPEG_KIT_MEDIA_INFORMATION_PARSER_H
#import <Foundation/Foundation.h>
#import "MediaInformation.h"
#import <Foundation/Foundation.h>
/**
* A parser that constructs MediaInformation from FFprobe's json output.
@ -34,16 +34,16 @@
* @param ffprobeJsonOutput FFprobe json output
* @return created MediaInformation instance of nil if a parsing error occurs
*/
+ (MediaInformation*)from:(NSString*)ffprobeJsonOutput;
+ (MediaInformation *)from:(NSString *)ffprobeJsonOutput;
/**
* Extracts <code>MediaInformation</code> from the given FFprobe json output. If a parsing error occurs an NSException
* is thrown.
* Extracts <code>MediaInformation</code> from the given FFprobe json output. If
* a parsing error occurs an NSException is thrown.
*
* @param ffprobeJsonOutput FFprobe json output
* @return created MediaInformation instance
*/
+ (MediaInformation*)fromWithError:(NSString*)ffprobeJsonOutput;
+ (MediaInformation *)fromWithError:(NSString *)ffprobeJsonOutput;
@end

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

Loading…
Cancel
Save