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. 441
      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. 99
      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. 919
      apple/src/FFmpegKitConfig.m
  84. 56
      apple/src/FFmpegSession.h
  85. 97
      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. 8
      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. If applicable, add screenshots to help explain your problem.
**Logs** **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** **Environment**
- Platform: `Android`/`Flutter`/`iOS`/`Linux`/`macOS`/`ReactNative`/`tvOS` - 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` - 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` - Version: ``
- Source branch: `main`, `development` - Source branch: ``
- Xcode version: `13.4.1` - Xcode version: ``
- Cocoapods version: `1.10.3` - Cocoapods version: ``
- Android Studio version: `4.0` - Android Studio version: ``
- Android NDK version: `r25b` - Android NDK version: ``
- flutter doctor: ` ` - flutter doctor: ``
- react-native info: ` ` - react-native info: ``
**Other** **Other**
Add any other context about the problem here. Add any other context about the problem here.

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

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

@ -41,24 +41,6 @@ jobs:
- name: print ffbuild logs - name: print ffbuild logs
if: ${{ failure() }} if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log' 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: build-main-on-ubuntu-2004:
name: linux main on ubuntu-20.04 name: linux main on ubuntu-20.04
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@ -77,21 +59,3 @@ jobs:
- name: print ffbuild logs - name: print ffbuild logs
if: ${{ failure() }} if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log' 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 runs-on: macos-14
strategy: strategy:
matrix: matrix:
xcode: [ '15.4', '16.0' ] xcode: [ '15.3', '15.4' ]
defaults: defaults:
run: run:
shell: bash shell: bash
@ -73,12 +73,12 @@ jobs:
- name: print ffbuild logs - name: print ffbuild logs
if: ${{ failure() }} if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log' run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-lts-on-macos-monterey: build-min-sdk-1012-on-macos-ventura:
name: macos lts on monterey name: macos min sdk 10.12 on ventura
runs-on: macos-12 runs-on: macos-13
strategy: strategy:
matrix: matrix:
xcode: [ '13.4.1', '14.2' ] xcode: [ '14.3.1', '15.2' ]
defaults: defaults:
run: run:
shell: bash shell: bash
@ -89,7 +89,7 @@ jobs:
- name: set up xcode - name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script - 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 - name: print build logs
if: ${{ always() }} if: ${{ always() }}
run: cat build.log run: cat build.log

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
strategy: strategy:
matrix: 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' ] branches: [ 'main', 'development' ]
defaults: defaults:
run: run:
@ -43,12 +43,12 @@ jobs:
- name: print ffbuild logs - name: print ffbuild logs
if: ${{ failure() }} if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log' run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-android-lts-on-linux: build-android-api-16-on-linux:
name: android lts on linux name: android api 16 on linux
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
strategy: strategy:
matrix: matrix:
ndk-version: [ 'r22b-linux-x86_64', 'r23b-linux' ] ndk-version: [ 'r22b-linux-x86_64' ]
branches: [ 'main', 'development' ] branches: [ 'main', 'development' ]
defaults: defaults:
run: run:
@ -74,7 +74,7 @@ jobs:
unzip -q -o ndk.zip -d .ndk unzip -q -o ndk.zip -d .ndk
echo "ANDROID_NDK_ROOT=$PWD/.ndk/$(ls .ndk)" >> $GITHUB_ENV echo "ANDROID_NDK_ROOT=$PWD/.ndk/$(ls .ndk)" >> $GITHUB_ENV
- name: run the build script - 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 - name: print build logs
if: ${{ always() }} if: ${{ always() }}
run: cat build.log run: cat build.log

@ -10,7 +10,7 @@ jobs:
runs-on: macos-14 runs-on: macos-14
strategy: strategy:
matrix: matrix:
xcode: [ '15.4', '16.0' ] xcode: [ '15.3', '15.4' ]
branches: [ 'main', 'development' ] branches: [ 'main', 'development' ]
defaults: defaults:
run: run:
@ -31,12 +31,12 @@ jobs:
- name: print ffbuild logs - name: print ffbuild logs
if: ${{ failure() }} if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log' run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-ios-lts-on-macos-monterey: build-ios-min-sdk-10-on-macos-ventura:
name: ios lts on monterey name: ios min sdk 10 on ventura
runs-on: macos-12 runs-on: macos-13
strategy: strategy:
matrix: matrix:
xcode: [ '13.4.1' ] xcode: [ '15.2' ]
branches: [ 'main', 'development' ] branches: [ 'main', 'development' ]
defaults: defaults:
run: run:
@ -50,7 +50,7 @@ jobs:
- name: set up xcode - name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script - 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 - name: print build logs
if: ${{ always() }} if: ${{ always() }}
run: cat build.log run: cat build.log
@ -62,7 +62,7 @@ jobs:
runs-on: macos-14 runs-on: macos-14
strategy: strategy:
matrix: matrix:
xcode: [ '15.4', '16.0' ] xcode: [ '15.3', '15.4' ]
branches: [ 'main', 'development' ] branches: [ 'main', 'development' ]
defaults: defaults:
run: run:
@ -83,12 +83,12 @@ jobs:
- name: print ffbuild logs - name: print ffbuild logs
if: ${{ failure() }} if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log' run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-macos-lts-on-macos-monterey: build-macos-min-sdk-1012-on-macos-ventura:
name: macos lts on monterey name: macos min sdk 10.12 on ventura
runs-on: macos-12 runs-on: macos-13
strategy: strategy:
matrix: matrix:
xcode: [ '13.4.1' ] xcode: [ '15.2' ]
branches: [ 'main', 'development' ] branches: [ 'main', 'development' ]
defaults: defaults:
run: run:
@ -102,7 +102,7 @@ jobs:
- name: set up xcode - name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script - 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 - name: print build logs
if: ${{ always() }} if: ${{ always() }}
run: cat build.log run: cat build.log
@ -114,7 +114,7 @@ jobs:
runs-on: macos-14 runs-on: macos-14
strategy: strategy:
matrix: matrix:
xcode: [ '15.4', '16.0' ] xcode: [ '15.3', '15.4' ]
branches: [ 'main', 'development' ] branches: [ 'main', 'development' ]
defaults: defaults:
run: run:
@ -135,12 +135,12 @@ jobs:
- name: print ffbuild logs - name: print ffbuild logs
if: ${{ failure() }} if: ${{ failure() }}
run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log' run: '[[ -f ./src/ffmpeg/ffbuild/config.log ]] && tail -50 ./src/ffmpeg/ffbuild/config.log'
build-tvos-lts-on-macos-monterey: build-tvos-min-sdk-100-on-macos-ventura:
name: tvos lts on monterey name: tvos min sdk 10.0 on ventura
runs-on: macos-12 runs-on: macos-13
strategy: strategy:
matrix: matrix:
xcode: [ '13.4.1' ] xcode: [ '15.2' ]
branches: [ 'main', 'development' ] branches: [ 'main', 'development' ]
defaults: defaults:
run: run:
@ -154,7 +154,7 @@ jobs:
- name: set up xcode - name: set up xcode
run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh run: echo "export DEVELOPER_DIR=/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer" > ~/.xcode.for.ffmpeg.kit.sh
- name: run the build script - 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 - name: print build logs
if: ${{ always() }} if: ${{ always() }}
run: cat build.log run: cat build.log

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

1
.gitignore vendored

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

@ -17,7 +17,7 @@ source "${BASEDIR}"/scripts/variable.sh
source "${BASEDIR}"/scripts/function-${FFMPEG_KIT_BUILD_TYPE}.sh source "${BASEDIR}"/scripts/function-${FFMPEG_KIT_BUILD_TYPE}.sh
disabled_libraries=() disabled_libraries=()
# SET DEFAULTS SETTINGS # SET DEFAULT SETTINGS
enable_default_android_architectures enable_default_android_architectures
enable_default_android_libraries enable_default_android_libraries
enable_main_build enable_main_build
@ -34,18 +34,6 @@ BUILD_FULL=""
BUILD_TYPE_ID="" BUILD_TYPE_ID=""
BUILD_VERSION=$(git describe --tags --always 2>>"${BASEDIR}"/build.log) 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 # PROCESS BUILD OPTIONS
while [ ! $# -eq 0 ]; do while [ ! $# -eq 0 ]; do
@ -82,10 +70,13 @@ while [ ! $# -eq 0 ]; do
-s | --speed) -s | --speed)
optimize_for_speed optimize_for_speed
;; ;;
-l | --lts) ;;
-f | --force) -f | --force)
export BUILD_FORCE="1" export BUILD_FORCE="1"
;; ;;
--jobs=*)
JOB_COUNT=$(echo $1 | sed -e 's/^--[A-Za-z]*=//g')
export BUILD_JOBS="${JOB_COUNT}"
;;
--reconf-*) --reconf-*)
CONF_LIBRARY=$(echo $1 | sed -e 's/^--[A-Za-z]*-//g') CONF_LIBRARY=$(echo $1 | sed -e 's/^--[A-Za-z]*-//g')
@ -138,6 +129,30 @@ while [ ! $# -eq 0 ]; do
--no-ffmpeg-kit-protocols) --no-ffmpeg-kit-protocols)
export NO_FFMPEG_KIT_PROTOCOLS="1" 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" print_unknown_option "$1"
;; ;;
@ -150,6 +165,12 @@ if [[ -z ${BUILD_VERSION} ]]; then
exit 1 exit 1
fi 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 # PROCESS FULL OPTION AS LAST OPTION
if [[ -n ${BUILD_FULL} ]]; then if [[ -n ${BUILD_FULL} ]]; then
for library in {0..61}; do for library in {0..61}; do
@ -164,7 +185,7 @@ if [[ -n ${BUILD_FULL} ]]; then
fi fi
# DISABLE SPECIFIED LIBRARIES # DISABLE SPECIFIED LIBRARIES
for disabled_library in ${disabled_libraries[@]}; do for disabled_library in "${disabled_libraries[@]}"; do
set_library "${disabled_library}" 0 set_library "${disabled_library}" 0
done done
@ -175,7 +196,7 @@ if [[ -n ${DISPLAY_HELP} ]]; then
fi fi
# SET API LEVEL IN build.gradle # 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 ${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" 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 fi
done done
trap fail_operation EXIT
echo -n -e "\nDownloading sources: " echo -n -e "\nDownloading sources: "
echo -e "INFO: Downloading the source code of ffmpeg and external libraries.\n" 1>>"${BASEDIR}"/build.log 2>&1 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 fi
export ARCH=$(get_arch_name $run_arch) export ARCH=$(get_arch_name $run_arch)
export TOOLCHAIN=$(get_toolchain)
export TOOLCHAIN_ARCH=$(get_toolchain_arch)
# EXECUTE MAIN BUILD SCRIPT # EXECUTE MAIN BUILD SCRIPT
. "${BASEDIR}"/scripts/main-android.sh "${ENABLED_LIBRARIES[@]}" || exit 1 . "${BASEDIR}"/scripts/main-android.sh "${ENABLED_LIBRARIES[@]}" || exit 1
@ -246,20 +266,22 @@ done
export API=${ORIGINAL_API} export API=${ORIGINAL_API}
# SET ARCHITECTURES TO BUILD # SET ARCHITECTURES TO BUILD
rm -f "${BASEDIR}"/android/build/.armv7 1>>"${BASEDIR}"/build.log 2>&1 create_file "${BASEDIR}"/android/jni/build.mk "API := ${API}"
rm -f "${BASEDIR}"/android/build/.armv7neon 1>>"${BASEDIR}"/build.log 2>&1
rm -f "${BASEDIR}"/android/build/.lts 1>>"${BASEDIR}"/build.log 2>&1
ANDROID_ARCHITECTURES="" ANDROID_ARCHITECTURES=""
if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A]} -eq 1 ]] || [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A_NEON]} -eq 1 ]]; then if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A]} -eq 1 ]] || [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A_NEON]} -eq 1 ]]; then
ANDROID_ARCHITECTURES+="$(get_android_arch 0) " ANDROID_ARCHITECTURES+="$(get_android_arch 0) "
fi fi
if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A]} -eq 1 ]]; then if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A]} -eq 1 ]]; then
mkdir -p "${BASEDIR}"/android/build 1>>"${BASEDIR}"/build.log 2>&1 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 fi
if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A_NEON]} -eq 1 ]]; then if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM_V7A_NEON]} -eq 1 ]]; then
mkdir -p "${BASEDIR}"/android/build 1>>"${BASEDIR}"/build.log 2>&1 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 fi
if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM64_V8A]} -eq 1 ]]; then if [[ ${ENABLED_ARCHITECTURES[ARCH_ARM64_V8A]} -eq 1 ]]; then
ANDROID_ARCHITECTURES+="$(get_android_arch 2) " ANDROID_ARCHITECTURES+="$(get_android_arch 2) "
@ -270,9 +292,15 @@ fi
if [[ ${ENABLED_ARCHITECTURES[ARCH_X86_64]} -eq 1 ]]; then if [[ ${ENABLED_ARCHITECTURES[ARCH_X86_64]} -eq 1 ]]; then
ANDROID_ARCHITECTURES+="$(get_android_arch 4) " ANDROID_ARCHITECTURES+="$(get_android_arch 4) "
fi fi
if [[ ! -z ${FFMPEG_KIT_LTS_BUILD} ]]; then append_file "${BASEDIR}"/android/jni/build.mk "ARMV7_BUILD_PATH := android-arm-${API}"
mkdir -p "${BASEDIR}"/android/build 1>>"${BASEDIR}"/build.log 2>&1 append_file "${BASEDIR}"/android/jni/build.mk "ARMV7_NEON_BUILD_PATH := android-arm-neon-${API}"
create_file "${BASEDIR}"/android/build/.lts 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 fi
# BUILD FFMPEG-KIT # BUILD FFMPEG-KIT
@ -300,8 +328,7 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
RC=$(copy_external_library_license_file ${library} "${LICENSE_FILE}") RC=$(copy_external_library_license_file ${library} "${LICENSE_FILE}")
if [[ ${RC} -ne 0 ]]; then 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 "ERROR: 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"
exit 1 exit 1
fi fi
@ -321,8 +348,7 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
RC=$? RC=$?
if [[ ${RC} -ne 0 ]]; then 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 "ERROR: 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"
exit 1 exit 1
fi fi
@ -344,7 +370,9 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
# BUILD NATIVE LIBRARY # BUILD NATIVE LIBRARY
if [[ ${SKIP_ffmpeg_kit} -ne 1 ]]; then 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 arch -x86_64 "${ANDROID_NDK_ROOT}"/ndk-build -B 1>>"${BASEDIR}"/build.log 2>&1
else else
"${ANDROID_NDK_ROOT}"/ndk-build -B 1>>"${BASEDIR}"/build.log 2>&1 "${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 if [ $? -eq 0 ]; then
echo "ok" echo "ok"
else else
echo "failed"
exit 1 exit 1
fi fi
else 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 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 ./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 if [ $? -ne 0 ]; then
echo -e "failed\n"
exit 1 exit 1
fi fi
@ -381,7 +407,6 @@ if [[ -n ${ANDROID_ARCHITECTURES} ]]; then
mkdir -p "${FFMPEG_KIT_AAR}" 1>>"${BASEDIR}"/build.log 2>&1 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 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 if [ $? -ne 0 ]; then
echo -e "failed\n"
exit 1 exit 1
fi fi

@ -4,7 +4,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { 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 # could be handy for archiving the generated documentation or if some version
# control system is used. # 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 # 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 # 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")) { if (project.android.hasProperty("namespace")) {
namespace 'com.arthenica.ffmpegkit' namespace 'com.arthenica.ffmpegkit'
} }
compileSdk 33 compileSdk 34
ndkVersion "22.1.7171670"
defaultConfig { defaultConfig {
minSdk 24 minSdk 24
targetSdk 33 targetSdk 34
versionCode 240600 versionCode 240612
versionName "6.0" versionName "6.1.2"
project.archivesBaseName = "ffmpeg-kit" project.archivesBaseName = "ffmpeg-kit"
consumerProguardFiles "consumer-rules.pro" consumerProguardFiles "consumer-rules.pro"
} }
@ -60,6 +59,6 @@ task javadoc(type: Javadoc) {
dependencies { dependencies {
api 'com.arthenica:smart-exception-java:0.2.1' api 'com.arthenica:smart-exception-java:0.2.1'
testImplementation "androidx.test.ext:junit:1.1.5" testImplementation "androidx.test.ext:junit:1.2.1"
testImplementation 'org.json:json:20230618' testImplementation 'org.json:json:20240303'
} }

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

@ -26,44 +26,50 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#include <math.h>
#include <errno.h> #include <errno.h>
#include <malloc.h> #include <malloc.h>
#include <math.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// posix_memalign() and memalign() are not available in the NDK
// __INTRODUCED_IN(17)
#if __ANDROID_API__ < 17 #if __ANDROID_API__ < 17
int posix_memalign(void** memptr, size_t alignment, size_t size) { int posix_memalign(void **memptr, size_t alignment, size_t size) {
if ((alignment & (alignment - 1)) != 0 || alignment == 0) { if ((alignment & (alignment - 1)) != 0 || alignment == 0) {
return EINVAL; return EINVAL;
} }
if (alignment % sizeof(void*) != 0) { if (alignment % sizeof(void *) != 0) {
return EINVAL; return EINVAL;
} }
*memptr = memalign(alignment, size); *memptr = memalign(alignment, size);
if (*memptr == NULL) { if (*memptr == NULL) {
return errno; return errno;
} }
return 0; return 0;
} }
#endif /* __ANDROID_API__ < 17 */ #endif /* __ANDROID_API__ < 17 */
double log2(double x) { // log2() and log2f() are C99 functions, but they're not available in the NDK
return (log(x) / M_LN2); // __INTRODUCED_IN(18)
}
float log2f(float x) { #if __ANDROID_API__ < 18
return (float) log2((double) x);
} double log2(double x) { return (log(x) / M_LN2); }
float log2f(float x) { return (float)log2((double)x); }
#endif /* __ANDROID_API__ < 18 */
#ifdef __cplusplus #ifdef __cplusplus
}; /* end of extern "C" */ }; /* end of extern "C" */
#endif #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) 2018-2021 Taner Sener
* Copyright (c) 2024 ARTHENICA LTD
* *
* This file is part of FFmpegKit. * This file is part of FFmpegKit.
* *
@ -19,37 +20,37 @@
#include <pthread.h> #include <pthread.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include "config.h" #include "config.h"
#include "ffmpegkit.h"
#include "ffprobekit.h"
#include "fftools_ffmpeg.h"
#include "libavcodec/jni.h" #include "libavcodec/jni.h"
#include "libavutil/bprint.h" #include "libavutil/bprint.h"
#include "libavutil/file.h" #include "libavutil/file.h"
#include "fftools_ffmpeg.h"
#include "ffmpegkit.h"
#include "ffprobekit.h"
# define LogType 1 #define LogType 1
# define StatisticsType 2 #define StatisticsType 2
/** Callback data structure */ /** Callback data structure */
struct CallbackData { struct CallbackData {
int type; // 1 (log callback) or 2 (statistics callback) int type; // 1 (log callback) or 2 (statistics callback)
long sessionId; // session identifier long sessionId; // session identifier
int logLevel; // log level int logLevel; // log level
AVBPrint logData; // log data AVBPrint logData; // log data
int statisticsFrameNumber; // statistics frame number int statisticsFrameNumber; // statistics frame number
float statisticsFps; // statistics fps float statisticsFps; // statistics fps
float statisticsQuality; // statistics quality float statisticsQuality; // statistics quality
int64_t statisticsSize; // statistics size int64_t statisticsSize; // statistics size
double statisticsTime; // statistics time double statisticsTime; // statistics time
double statisticsBitrate; // statistics bitrate double statisticsBitrate; // statistics bitrate
double statisticsSpeed; // statistics speed double statisticsSpeed; // statistics speed
struct CallbackData *next; struct CallbackData *next;
}; };
/** Session control variables */ /** Session control variables */
@ -113,21 +114,41 @@ int configuredLogLevel = AV_LOG_INFO;
/** Prototypes of native functions defined by Config class. */ /** Prototypes of native functions defined by Config class. */
JNINativeMethod configMethods[] = { JNINativeMethod configMethods[] = {
{"enableNativeRedirection", "()V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection}, {"enableNativeRedirection", "()V",
{"disableNativeRedirection", "()V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_disableNativeRedirection}, (void *)
{"setNativeLogLevel", "(I)V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLogLevel}, Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNativeRedirection},
{"getNativeLogLevel", "()I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLogLevel}, {"disableNativeRedirection", "()V",
{"getNativeFFmpegVersion", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeFFmpegVersion}, (void *)
{"getNativeVersion", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeVersion}, Java_com_arthenica_ffmpegkit_FFmpegKitConfig_disableNativeRedirection},
{"nativeFFmpegExecute", "(J[Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegExecute}, {"setNativeLogLevel", "(I)V",
{"nativeFFmpegCancel", "(J)V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegCancel}, (void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLogLevel},
{"nativeFFprobeExecute", "(J[Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute}, {"getNativeLogLevel", "()I",
{"registerNewNativeFFmpegPipe", "(Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNativeFFmpegPipe}, (void *)Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLogLevel},
{"getNativeBuildDate", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeBuildDate}, {"getNativeFFmpegVersion", "()Ljava/lang/String;",
{"setNativeEnvironmentVariable", "(Ljava/lang/String;Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvironmentVariable}, (void *)
{"ignoreNativeSignal", "(I)V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNativeSignal}, Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeFFmpegVersion},
{"messagesInTransmit", "(J)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit} {"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 */ /** Forward declaration for function defined in fftools_ffmpeg.c */
int ffmpeg_execute(int argc, char **argv); 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(); int flags = av_log_get_flags();
AVClass* avc = avcl ? *(AVClass **) avcl : NULL; AVClass *avc = avcl ? *(AVClass **)avcl : NULL;
av_bprint_init(part+0, 0, 1); av_bprint_init(part + 0, 0, 1);
av_bprint_init(part+1, 0, 1); av_bprint_init(part + 1, 0, 1);
av_bprint_init(part+2, 0, 1); av_bprint_init(part + 2, 0, 1);
av_bprint_init(part+3, 0, 65536); av_bprint_init(part + 3, 0, 65536);
if (*print_prefix && avc) { if (*print_prefix && avc) {
if (avc->parent_log_context_offset) { if (avc->parent_log_context_offset) {
AVClass** parent = *(AVClass ***) (((uint8_t *) avcl) + AVClass **parent = *(AVClass ***)(((uint8_t *)avcl) +
avc->parent_log_context_offset); avc->parent_log_context_offset);
if (parent && *parent) { if (parent && *parent) {
av_bprintf(part+0, "[%s @ %p] ", av_bprintf(part + 0, "[%s @ %p] ", (*parent)->item_name(parent),
(*parent)->item_name(parent), parent); parent);
} }
} }
av_bprintf(part+1, "[%s @ %p] ", av_bprintf(part + 1, "[%s @ %p] ", avc->item_name(avcl), avcl);
avc->item_name(avcl), avcl);
} }
if (*print_prefix && (level > AV_LOG_QUIET) && (flags & AV_LOG_PRINT_LEVEL)) 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) { 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; 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'; *print_prefix = lastc == '\n' || lastc == '\r';
} }
} }
static void avutil_log_sanitize(uint8_t *line) { static void avutil_log_sanitize(uint8_t *line) {
while(*line){ while (*line) {
if(*line < 0x08 || (*line > 0x0D && *line < 0x20)) if (*line < 0x08 || (*line > 0x0D && *line < 0x20))
*line='?'; *line = '?';
line++; line++;
} }
} }
@ -222,22 +246,16 @@ void monitorInit() {
pthread_condattr_destroy(&cattributes); pthread_condattr_destroy(&cattributes);
} }
void mutexUnInit() { void mutexUnInit() { pthread_mutex_destroy(&lockMutex); }
pthread_mutex_destroy(&lockMutex);
}
void monitorUnInit() { void monitorUnInit() {
pthread_mutex_destroy(&monitorMutex); pthread_mutex_destroy(&monitorMutex);
pthread_cond_destroy(&monitorCondition); pthread_cond_destroy(&monitorCondition);
} }
void mutexLock() { void mutexLock() { pthread_mutex_lock(&lockMutex); }
pthread_mutex_lock(&lockMutex);
}
void mutexUnlock() { void mutexUnlock() { pthread_mutex_unlock(&lockMutex); }
pthread_mutex_unlock(&lockMutex);
}
void monitorWait(int milliSeconds) { void monitorWait(int milliSeconds) {
struct timeval tp; struct timeval tp;
@ -249,10 +267,10 @@ void monitorWait(int milliSeconds) {
return; return;
} }
ts.tv_sec = tp.tv_sec; ts.tv_sec = tp.tv_sec;
ts.tv_nsec = tp.tv_usec * 1000; ts.tv_nsec = tp.tv_usec * 1000;
ts.tv_sec += milliSeconds / 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_sec += ts.tv_nsec / 1000000000L;
ts.tv_nsec = ts.tv_nsec % 1000000000L; ts.tv_nsec = ts.tv_nsec % 1000000000L;
@ -276,7 +294,8 @@ void monitorNotify() {
void logCallbackDataAdd(int level, AVBPrint *data) { void logCallbackDataAdd(int level, AVBPrint *data) {
// CREATE DATA STRUCT FIRST // 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->type = LogType;
newData->sessionId = globalSessionId; newData->sessionId = globalSessionId;
newData->logLevel = level; newData->logLevel = level;
@ -291,7 +310,8 @@ void logCallbackDataAdd(int level, AVBPrint *data) {
callbackDataTail = newData; callbackDataTail = newData;
if (callbackDataHead != NULL) { 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 { } else {
callbackDataHead = newData; callbackDataHead = newData;
} }
@ -306,16 +326,21 @@ void logCallbackDataAdd(int level, AVBPrint *data) {
monitorNotify(); 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. * 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 // 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->type = StatisticsType;
newData->sessionId = globalSessionId; newData->sessionId = globalSessionId;
newData->statisticsFrameNumber = frameNumber; newData->statisticsFrameNumber = frameNumber;
@ -335,7 +360,8 @@ void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_
callbackDataTail = newData; callbackDataTail = newData;
if (callbackDataHead != NULL) { 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 { } else {
callbackDataHead = newData; callbackDataHead = newData;
} }
@ -350,7 +376,9 @@ void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_
monitorNotify(); 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; struct CallbackData *nextHead = currentData->next;
if (nextHead == NULL) { if (nextHead == NULL) {
if (callbackDataHead != callbackDataTail) { 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 { } else {
callbackDataTail = NULL; 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 * @param id session id
* @return 1 if exists, false otherwise * @return 1 if exists, false otherwise
@ -443,7 +474,8 @@ void resetMessagesInTransmit(long id) {
* @param format format string * @param format format string
* @param vargs arguments * @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 fullLine;
AVBPrint part[4]; AVBPrint part[4];
int print_prefix = 1; 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(); int activeLogLevel = av_log_get_level();
// AV_LOG_STDERR logs are always redirected // 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; return;
} }
@ -467,16 +500,17 @@ void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, v
avutil_log_sanitize(part[3].str); avutil_log_sanitize(part[3].str);
// COMBINE ALL 4 LOG PARTS // 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) { if (fullLine.len > 0) {
logCallbackDataAdd(level, &fullLine); logCallbackDataAdd(level, &fullLine);
} }
av_bprint_finalize(part, NULL); av_bprint_finalize(part, NULL);
av_bprint_finalize(part+1, NULL); av_bprint_finalize(part + 1, NULL);
av_bprint_finalize(part+2, NULL); av_bprint_finalize(part + 2, NULL);
av_bprint_finalize(part+3, NULL); av_bprint_finalize(part + 3, NULL);
av_bprint_finalize(&fullLine, 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 bitrate output bit rate in kbits/s
* @param speed processing speed = processed duration / operation duration * @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) { void ffmpegkit_statistics_callback_function(int frameNumber, float fps,
statisticsCallbackDataAdd(frameNumber, fps, quality, size, time, bitrate, speed); 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() { void *callbackThreadFunction() {
JNIEnv *env; 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_OK) {
if (getEnvRc != JNI_EDETACHED) { 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; return NULL;
} }
if ((*globalVm)->AttachCurrentThread(globalVm, &env, NULL) != 0) { 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; return NULL;
} }
} }
LOGD("Async callback block started.\n"); LOGD("Async callback block started.\n");
while(redirectionEnabled) { while (redirectionEnabled) {
struct CallbackData *callbackData = callbackDataRemove(); struct CallbackData *callbackData = callbackDataRemove();
if (callbackData != NULL) { if (callbackData != NULL) {
@ -525,9 +567,13 @@ void *callbackThreadFunction() {
int size = callbackData->logData.len; int size = callbackData->logData.len;
jbyteArray byteArray = (jbyteArray) (*env)->NewByteArray(env, size); jbyteArray byteArray =
(*env)->SetByteArrayRegion(env, byteArray, 0, size, callbackData->logData.str); (jbyteArray)(*env)->NewByteArray(env, size);
(*env)->CallStaticVoidMethod(env, configClass, logMethod, (jlong) callbackData->sessionId, callbackData->logLevel, byteArray); (*env)->SetByteArrayRegion(env, byteArray, 0, size,
callbackData->logData.str);
(*env)->CallStaticVoidMethod(env, configClass, logMethod,
(jlong)callbackData->sessionId,
callbackData->logLevel, byteArray);
(*env)->DeleteLocalRef(env, byteArray); (*env)->DeleteLocalRef(env, byteArray);
// CLEAN LOG DATA // CLEAN LOG DATA
@ -537,15 +583,21 @@ void *callbackThreadFunction() {
// STATISTICS CALLBACK // STATISTICS CALLBACK
(*env)->CallStaticVoidMethod(env, configClass, statisticsMethod, (*env)->CallStaticVoidMethod(
(jlong) callbackData->sessionId, callbackData->statisticsFrameNumber, env, configClass, statisticsMethod,
callbackData->statisticsFps, callbackData->statisticsQuality, (jlong)callbackData->sessionId,
callbackData->statisticsFrameNumber,
callbackData->statisticsFps,
callbackData->statisticsQuality,
callbackData->statisticsSize, callbackData->statisticsTime, 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 // CLEAN STRUCT
callbackData->next = NULL; 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) { int saf_open(int safId) {
JNIEnv *env = NULL; JNIEnv *env = NULL;
(*globalVm)->GetEnv(globalVm, (void**) &env, JNI_VERSION_1_6); bool attached = false;
return (*env)->CallStaticIntMethod(env, configClass, safOpenMethod, safId); 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) { int saf_close(int fd) {
JNIEnv *env = NULL; JNIEnv *env = NULL;
(*globalVm)->GetEnv(globalVm, (void**) &env, JNI_VERSION_1_6); bool attached = false;
return (*env)->CallStaticIntMethod(env, configClass, safCloseMethod, fd); 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) { jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env; 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); LOGE("OnLoad failed to GetEnv for class %s.\n", configClassName);
return JNI_FALSE; return JNI_FALSE;
} }
@ -625,8 +724,9 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_FALSE; return JNI_FALSE;
} }
if ((*env)->RegisterNatives(env, localConfigClass, configMethods, 14) < 0) { if ((*env)->RegisterNatives(env, localConfigClass, configMethods, 15) < 0) {
LOGE("OnLoad failed to RegisterNatives for class %s.\n", configClassName); LOGE("OnLoad failed to RegisterNatives for class %s.\n",
configClassName);
return JNI_FALSE; return JNI_FALSE;
} }
@ -638,31 +738,37 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
(*env)->GetJavaVM(env, &globalVm); (*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) { if (logMethod == NULL) {
LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "log"); LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "log");
return JNI_FALSE; return JNI_FALSE;
} }
statisticsMethod = (*env)->GetStaticMethodID(env, localConfigClass, "statistics", "(JIFFJDDD)V"); statisticsMethod = (*env)->GetStaticMethodID(env, localConfigClass,
"statistics", "(JIFFJDDD)V");
if (statisticsMethod == NULL) { 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; return JNI_FALSE;
} }
safOpenMethod = (*env)->GetStaticMethodID(env, localConfigClass, "safOpen", "(I)I"); safOpenMethod =
(*env)->GetStaticMethodID(env, localConfigClass, "safOpen", "(I)I");
if (safOpenMethod == NULL) { if (safOpenMethod == NULL) {
LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "safOpen"); LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "safOpen");
return JNI_FALSE; return JNI_FALSE;
} }
safCloseMethod = (*env)->GetStaticMethodID(env, localConfigClass, "safClose", "(I)I"); safCloseMethod =
(*env)->GetStaticMethodID(env, localConfigClass, "safClose", "(I)I");
if (safCloseMethod == NULL) { if (safCloseMethod == NULL) {
LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "safClose"); LOGE("OnLoad thread failed to GetStaticMethodID for %s.\n", "safClose");
return JNI_FALSE; 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) { if (stringConstructor == NULL) {
LOGE("OnLoad thread failed to GetMethodID for %s.\n", "<init>"); LOGE("OnLoad thread failed to GetMethodID for %s.\n", "<init>");
return JNI_FALSE; return JNI_FALSE;
@ -670,13 +776,13 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
av_jni_set_java_vm(vm, NULL); av_jni_set_java_vm(vm, NULL);
configClass = (jclass) ((*env)->NewGlobalRef(env, localConfigClass)); configClass = (jclass)((*env)->NewGlobalRef(env, localConfigClass));
stringClass = (jclass) ((*env)->NewGlobalRef(env, localStringClass)); stringClass = (jclass)((*env)->NewGlobalRef(env, localStringClass));
callbackDataHead = NULL; callbackDataHead = NULL;
callbackDataTail = 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(&sessionMap[i], 0);
atomic_init(&sessionInTransitMessageCountMap[i], 0); atomic_init(&sessionInTransitMessageCountMap[i], 0);
} }
@ -686,8 +792,10 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
redirectionEnabled = 0; redirectionEnabled = 0;
#ifdef USES_FFMPEG_KIT_PROTOCOLS
av_set_saf_open(saf_open); av_set_saf_open(saf_open);
av_set_saf_close(saf_close); av_set_saf_close(saf_close);
#endif
enableNativeRedirection(); 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 object reference to the class on which this method is invoked
* @param level log level * @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; configuredLogLevel = level;
} }
@ -711,7 +822,9 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeLog
* @param env pointer to native method interface * @param env pointer to native method interface
* @param object reference to the class on which this method is invoked * @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; return configuredLogLevel;
} }
@ -721,7 +834,9 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeLog
* @param env pointer to native method interface * @param env pointer to native method interface
* @param object reference to the class on which this method is invoked * @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(); enableNativeRedirection();
} }
@ -731,7 +846,9 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_enableNative
* @param env pointer to native method interface * @param env pointer to native method interface
* @param object reference to the class on which this method is invoked * @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(); 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 * @param object reference to the class on which this method is invoked
* @return FFmpeg version string * @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); 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 * @param object reference to the class on which this method is invoked
* @return FFmpegKit version string * @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); 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. * 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 * @param stringArray reference to the object holding FFmpeg command arguments
* @return zero on successful execution, non-zero on error * @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; jstring *tempArray = NULL;
int argumentCount = 1; int argumentCount = 1;
char **argv = NULL; char **argv = NULL;
@ -792,30 +937,33 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpeg
int programArgumentCount = (*env)->GetArrayLength(env, stringArray); int programArgumentCount = (*env)->GetArrayLength(env, stringArray);
argumentCount = programArgumentCount + 1; argumentCount = programArgumentCount + 1;
tempArray = (jstring *) av_malloc(sizeof(jstring) * programArgumentCount); tempArray =
(jstring *)av_malloc(sizeof(jstring) * programArgumentCount);
} }
/* PRESERVE USAGE FORMAT /* PRESERVE USAGE FORMAT
* *
* ffmpeg <arguments> * 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)); argv[0] = (char *)av_malloc(sizeof(char) * (strlen(LIB_NAME) + 1));
strcpy(argv[0], LIB_NAME); strcpy(argv[0], LIB_NAME);
// PREPARE ARRAY ELEMENTS // PREPARE ARRAY ELEMENTS
if (stringArray) { if (stringArray) {
for (int i = 0; i < (argumentCount - 1); i++) { 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) { 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 // REGISTER THE ID BEFORE STARTING THE SESSION
globalSessionId = (long) id; globalSessionId = (long)id;
addSession((long) id); addSession((long)id);
resetMessagesInTransmit(globalSessionId); resetMessagesInTransmit(globalSessionId);
@ -823,7 +971,7 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpeg
int returnCode = ffmpeg_execute(argumentCount, argv); int returnCode = ffmpeg_execute(argumentCount, argv);
// ALWAYS REMOVE THE ID FROM THE MAP // ALWAYS REMOVE THE ID FROM THE MAP
removeSession((long) id); removeSession((long)id);
// CLEANUP // CLEANUP
if (tempArray) { 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 object reference to the class on which this method is invoked
* @param id session id * @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); cancel_operation(id);
} }
@ -858,8 +1009,11 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpeg
* @param ffmpegPipePath full path of ffmpeg pipe * @param ffmpegPipePath full path of ffmpeg pipe
* @return zero on successful creation, non-zero on error * @return zero on successful creation, non-zero on error
*/ */
JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_registerNewNativeFFmpegPipe(JNIEnv *env, jclass object, jstring ffmpegPipePath) { JNIEXPORT int JNICALL
const char *ffmpegPipePathString = (*env)->GetStringUTFChars(env, ffmpegPipePath, 0); 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); 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 * @param object reference to the class on which this method is invoked
* @return FFmpegKit library build date * @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]; char buildDate[10];
sprintf(buildDate, "%d", FFMPEG_KIT_BUILD_DATE); sprintf(buildDate, "%d", FFMPEG_KIT_BUILD_DATE);
return (*env)->NewStringUTF(env, buildDate); return (*env)->NewStringUTF(env, buildDate);
@ -886,9 +1042,13 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNative
* @param variableValue environment variable value * @param variableValue environment variable value
* @return zero on success, non-zero on error * @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) { JNIEXPORT int JNICALL
const char *variableNameString = (*env)->GetStringUTFChars(env, variableName, 0); Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvironmentVariable(
const char *variableValueString = (*env)->GetStringUTFChars(env, variableValue, 0); 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); 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 env pointer to native method interface
* @param object reference to the class on which this method is invoked * @param object reference to the class on which this method is invoked
* @param signum signal number * @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) { if (signum == SIGQUIT) {
handleSIGQUIT = 0; handleSIGQUIT = 0;
} else if (signum == SIGINT) { } 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 * Returns the number of native messages which are not transmitted to the Java
* given session. * callbacks for the given session.
* *
* @param env pointer to native method interface * @param env pointer to native method interface
* @param object reference to the class on which this method is invoked * @param object reference to the class on which this method is invoked
* @param id session id * @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]); return atomic_load(&sessionInTransitMessageCountMap[id % SESSION_MAP_SIZE]);
} }

@ -20,20 +20,22 @@
#ifndef FFMPEG_KIT_H #ifndef FFMPEG_KIT_H
#define FFMPEG_KIT_H #define FFMPEG_KIT_H
#include <jni.h>
#include <android/log.h> #include <android/log.h>
#include <jni.h>
#include <string.h>
#include "libavutil/log.h"
#include "libavutil/ffversion.h" #include "libavutil/ffversion.h"
#include "libavutil/log.h"
/** Library version string */ /** Library version string */
#define FFMPEG_KIT_VERSION "6.0" #define FFMPEG_KIT_VERSION "6.1.2"
/** Defines tag used for Android logging. */ /** Defines tag used for Android logging. */
#define LIB_NAME "ffmpeg-kit" #define LIB_NAME "ffmpeg-kit"
/** Verbose Android logging macro. */ /** 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. */ /** Debug Android logging macro. */
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LIB_NAME, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LIB_NAME, __VA_ARGS__)
@ -52,90 +54,127 @@
* Method: enableNativeRedirection * Method: enableNativeRedirection
* Signature: ()V * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: disableNativeRedirection * Method: disableNativeRedirection
* Signature: ()V * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: setNativeLogLevel * Method: setNativeLogLevel
* Signature: (I)V * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativeLogLevel * Method: getNativeLogLevel
* Signature: ()I * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativeFFmpegVersion * Method: getNativeFFmpegVersion
* Signature: ()Ljava/lang/String; * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativeVersion * Method: getNativeVersion
* Signature: ()Ljava/lang/String; * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: nativeFFmpegExecute * Method: nativeFFmpegExecute
* Signature: (J[Ljava/lang/String;)I * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: nativeFFmpegCancel * Method: nativeFFmpegCancel
* Signature: (J)V * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: registerNewNativeFFmpegPipe * Method: registerNewNativeFFmpegPipe
* Signature: (Ljava/lang/String;)I * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: getNativeBuildDate * Method: getNativeBuildDate
* Signature: ()Ljava/lang/String; * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: setNativeEnvironmentVariable * Method: setNativeEnvironmentVariable
* Signature: (Ljava/lang/String;Ljava/lang/String;)I * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: ignoreNativeSignal * Method: ignoreNativeSignal
* Signature: (I)V * 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 * Class: com_arthenica_ffmpegkit_FFmpegKitConfig
* Method: messagesInTransmit * Method: messagesInTransmit
* Signature: (J)I * 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 */ #endif /* FFMPEG_KIT_H */

@ -17,20 +17,25 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>. * along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "ffmpegkit_abidetect.h"
#include "cpu-features.h" #include "cpu-features.h"
#include "fftools_ffmpeg.h" #include "fftools_ffmpeg.h"
#include "ffmpegkit_abidetect.h"
/** Full name of the Java class that owns native functions in this file. */ /** Full name of the Java class that owns native functions in this file. */
const char *abiDetectClassName = "com/arthenica/ffmpegkit/AbiDetect"; const char *abiDetectClassName = "com/arthenica/ffmpegkit/AbiDetect";
/** Prototypes of native functions defined by this file. */ /** Prototypes of native functions defined by this file. */
JNINativeMethod abiDetectMethods[] = { JNINativeMethod abiDetectMethods[] = {
{"getNativeAbi", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi}, {"getNativeAbi", "()Ljava/lang/String;",
{"getNativeCpuAbi", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_AbiDetect_getNativeCpuAbi}, (void *)Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi},
{"isNativeLTSBuild", "()Z", (void*) Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild}, {"getNativeCpuAbi", "()Ljava/lang/String;",
{"getNativeBuildConf", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_AbiDetect_getNativeBuildConf} (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. * Called when 'abidetect' native library is loaded.
@ -41,7 +46,7 @@ JNINativeMethod abiDetectMethods[] = {
*/ */
jint JNI_OnLoad(JavaVM *vm, void *reserved) { jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env; 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); LOGE("OnLoad failed to GetEnv for class %s.\n", abiDetectClassName);
return JNI_FALSE; return JNI_FALSE;
} }
@ -52,8 +57,9 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_FALSE; return JNI_FALSE;
} }
if ((*env)->RegisterNatives(env, abiDetectClass, abiDetectMethods, 4) < 0) { if ((*env)->RegisterNatives(env, abiDetectClass, abiDetectMethods, 5) < 0) {
LOGE("OnLoad failed to RegisterNatives for class %s.\n", abiDetectClassName); LOGE("OnLoad failed to RegisterNatives for class %s.\n",
abiDetectClassName);
return JNI_FALSE; 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 * @param object reference to the class on which this method is invoked
* @return loaded ABI name as UTF string * @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 #ifdef FFMPEG_KIT_ARM_V7A
return (*env)->NewStringUTF(env, "arm-v7a"); return (*env)->NewStringUTF(env, "arm-v7a");
@ -80,7 +87,6 @@ JNIEXPORT jstring JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_getNativeAbi(JN
#else #else
return (*env)->NewStringUTF(env, "unknown"); return (*env)->NewStringUTF(env, "unknown");
#endif #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 * @param object reference to the class on which this method is invoked
* @return ABI name of the running cpu as UTF string * @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(); AndroidCpuFamily family = android_getCpuFamily();
if (family == ANDROID_CPU_FAMILY_ARM) { 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 * @param object reference to the class on which this method is invoked
* @return yes or no * @return yes or no
*/ */
JNIEXPORT jboolean JNICALL Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild(JNIEnv *env, jclass object) { JNIEXPORT jboolean JNICALL
#if defined(FFMPEG_KIT_LTS) Java_com_arthenica_ffmpegkit_AbiDetect_isNativeLTSBuild(JNIEnv *env,
return JNI_TRUE; jclass object) {
#else #if defined(FFMPEG_KIT_LTS)
return JNI_FALSE; return JNI_TRUE;
#endif #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 * @param object reference to the class on which this method is invoked
* @return build configuration string * @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); 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 #ifndef FFMPEG_KIT_ABIDETECT_H
#define FFMPEG_KIT_ABIDETECT_H #define FFMPEG_KIT_ABIDETECT_H
#include <jni.h>
#include "ffmpegkit.h" #include "ffmpegkit.h"
#include <jni.h>
/** Represents armeabi-v7a ABI with NEON support. */ /** Represents armeabi-v7a ABI with NEON support. */
#define ABI_ARMV7A_NEON "armeabi-v7a-neon" #define ABI_ARMV7A_NEON "armeabi-v7a-neon"
@ -49,27 +49,40 @@
* Method: getNativeAbi * Method: getNativeAbi
* Signature: ()Ljava/lang/String; * 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 * Class: com_arthenica_ffmpegkit_AbiDetect
* Method: getNativeCpuAbi * Method: getNativeCpuAbi
* Signature: ()Ljava/lang/String; * 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 * Class: com_arthenica_ffmpegkit_AbiDetect
* Method: isNativeLTSBuild * Method: isNativeLTSBuild
* Signature: ()Z * 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 * Class: com_arthenica_ffmpegkit_AbiDetect
* Method: getNativeBuildConf * Method: getNativeBuildConf
* Signature: ()Ljava/lang/String; * 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 */ #endif /* FFMPEG_KIT_ABIDETECT_H */

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

@ -18,14 +18,14 @@
*/ */
#include <pthread.h> #include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include "config.h" #include "config.h"
#include "ffmpegkit.h"
#include "libavcodec/jni.h" #include "libavcodec/jni.h"
#include "libavutil/bprint.h" #include "libavutil/bprint.h"
#include "libavutil/mem.h" #include "libavutil/mem.h"
#include "ffmpegkit.h"
/** Forward declaration for function defined in fftools_ffprobe.c */ /** Forward declaration for function defined in fftools_ffprobe.c */
int ffprobe_execute(int argc, char **argv); 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 * @param stringArray reference to the object holding FFprobe command arguments
* @return zero on successful execution, non-zero on error * @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; jstring *tempArray = NULL;
int argumentCount = 1; int argumentCount = 1;
char **argv = NULL; char **argv = NULL;
@ -57,30 +59,33 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprob
int programArgumentCount = (*env)->GetArrayLength(env, stringArray); int programArgumentCount = (*env)->GetArrayLength(env, stringArray);
argumentCount = programArgumentCount + 1; argumentCount = programArgumentCount + 1;
tempArray = (jstring *) av_malloc(sizeof(jstring) * programArgumentCount); tempArray =
(jstring *)av_malloc(sizeof(jstring) * programArgumentCount);
} }
/* PRESERVE USAGE FORMAT /* PRESERVE USAGE FORMAT
* *
* ffprobe <arguments> * 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)); argv[0] = (char *)av_malloc(sizeof(char) * (strlen(LIB_NAME) + 1));
strcpy(argv[0], LIB_NAME); strcpy(argv[0], LIB_NAME);
// PREPARE ARRAY ELEMENTS // PREPARE ARRAY ELEMENTS
if (stringArray) { if (stringArray) {
for (int i = 0; i < (argumentCount - 1); i++) { 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) { 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 // REGISTER THE ID BEFORE STARTING THE SESSION
globalSessionId = (long) id; globalSessionId = (long)id;
addSession((long) id); addSession((long)id);
resetMessagesInTransmit(globalSessionId); resetMessagesInTransmit(globalSessionId);
@ -88,7 +93,7 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprob
int returnCode = ffprobe_execute(argumentCount, argv); int returnCode = ffprobe_execute(argumentCount, argv);
// ALWAYS REMOVE THE ID FROM THE MAP // ALWAYS REMOVE THE ID FROM THE MAP
removeSession((long) id); removeSession((long)id);
// CLEANUP // CLEANUP
if (tempArray) { if (tempArray) {

@ -27,6 +27,9 @@
* Method: nativeFFprobeExecute * Method: nativeFFprobeExecute
* Signature: (J[Ljava/lang/String;)I * 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 */ #endif /* FFPROBE_KIT_H */

@ -2,7 +2,7 @@
* Various utilities for command line tools * Various utilities for command line tools
* copyright (c) 2003 Fabrice Bellard * copyright (c) 2003 Fabrice Bellard
* copyright (c) 2018-2022 Taner Sener * copyright (c) 2018-2022 Taner Sener
* copyright (c) 2023 ARTHENICA LTD * copyright (c) 2023-2024 ARTHENICA LTD
* *
* This file is part of FFmpeg. * 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 * This file is the modified version of cmdutils.h file living in ffmpeg source
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * code under the fftools folder. We manually update it each time we depend on a
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries. * 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 * ffmpeg-kit changes by ARTHENICA LTD
* *
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
@ -40,12 +45,14 @@
* *
* 01.2020 * 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 * - AV_LOG_STDERR log level added
* *
* 12.2019 * 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 * 03.2019
* -------------------------------------------------------- * --------------------------------------------------------
@ -77,9 +84,10 @@
#endif #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(). * 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 hide_banner;
extern __thread int find_stream_info; 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. * Wraps exit with a program-specific cleanup routine.
*/ */
@ -129,12 +121,6 @@ void init_dynload(void);
*/ */
void uninit_opts(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 * Fallback for options that are not explicitly handled, these will be
* parsed through AVOptions. * 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. * 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 * @param context the context of the value to be set (e.g. the
* corresponding command line option name) * 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 min the minimum valid accepted value
* @param max the maximum valid accepted value * @param max the maximum valid accepted value
*/ */
double parse_number_or_die(const char *context, const char *numstr, int type, int parse_number(const char *context, const char *numstr, int type, double min,
double min, double max); double max, double *dst);
/**
* 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);
typedef struct SpecifierOpt { typedef struct SpecifierOpt {
char *specifier; /**< stream/chapter/program/... specifier */ char *specifier; /**< stream/chapter/program/... specifier */
union { union {
uint8_t *str; uint8_t *str;
int i; int i;
int64_t i64; int64_t i64;
uint64_t ui64; uint64_t ui64;
float f; float f;
double dbl; double dbl;
} u; } u;
} SpecifierOpt; } SpecifierOpt;
typedef struct OptionDef { typedef struct OptionDef {
const char *name; const char *name;
int flags; int flags;
#define HAS_ARG 0x0001 #define HAS_ARG 0x0001
#define OPT_BOOL 0x0002 #define OPT_BOOL 0x0002
#define OPT_EXPERT 0x0004 #define OPT_EXPERT 0x0004
#define OPT_STRING 0x0008 #define OPT_STRING 0x0008
#define OPT_VIDEO 0x0010 #define OPT_VIDEO 0x0010
#define OPT_AUDIO 0x0020 #define OPT_AUDIO 0x0020
#define OPT_INT 0x0080 #define OPT_INT 0x0080
#define OPT_FLOAT 0x0100 #define OPT_FLOAT 0x0100
#define OPT_SUBTITLE 0x0200 #define OPT_SUBTITLE 0x0200
#define OPT_INT64 0x0400 #define OPT_INT64 0x0400
#define OPT_EXIT 0x0800 #define OPT_EXIT 0x0800
#define OPT_DATA 0x1000 #define OPT_DATA 0x1000
#define OPT_PERFILE 0x2000 /* the option is per-file (currently ffmpeg-only). #define OPT_PERFILE \
implied by OPT_OFFSET or OPT_SPEC */ 0x2000 /* the option is per-file (currently ffmpeg-only). \
#define OPT_OFFSET 0x4000 /* option is specified as an offset in a passed optctx */ implied by OPT_OFFSET or OPT_SPEC */
#define OPT_SPEC 0x8000 /* option is to be stored in an array of SpecifierOpt. #define OPT_OFFSET \
Implies OPT_OFFSET. Next element after the offset is 0x4000 /* option is specified as an offset in a passed optctx */
an int containing element count in the array. */ #define OPT_SPEC \
#define OPT_TIME 0x10000 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_DOUBLE 0x20000
#define OPT_INPUT 0x40000 #define OPT_INPUT 0x40000
#define OPT_OUTPUT 0x80000 #define OPT_OUTPUT 0x80000
union { union {
void *dst_ptr; void *dst_ptr;
int (*func_arg)(void *, const char *, const char *); int (*func_arg)(void *, const char *, const char *);
size_t off; 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 * argument without a leading option name flag. NULL if such arguments do
* not have to be processed. * not have to be processed.
*/ */
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options, int parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
void (* parse_arg_function)(void *optctx, const char*)); int (*parse_arg_function)(void *optctx, const char *));
/** /**
* Parse one given option. * 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, int parse_option(void *optctx, const char *opt, const char *arg,
const OptionDef *options); const OptionDef *options);
@ -279,9 +250,9 @@ int parse_option(void *optctx, const char *opt, const char *arg,
* used multiple times. * used multiple times.
*/ */
typedef struct Option { typedef struct Option {
const OptionDef *opt; const OptionDef *opt;
const char *key; const char *key;
const char *val; const char *val;
} Option; } Option;
typedef struct OptionGroupDef { typedef struct OptionGroupDef {
@ -304,7 +275,7 @@ typedef struct OptionGroup {
const char *arg; const char *arg;
Option *opts; Option *opts;
int nb_opts; int nb_opts;
AVDictionary *codec_opts; AVDictionary *codec_opts;
AVDictionary *format_opts; AVDictionary *format_opts;
@ -320,14 +291,14 @@ typedef struct OptionGroupList {
const OptionGroupDef *group_def; const OptionGroupDef *group_def;
OptionGroup *groups; OptionGroup *groups;
int nb_groups; int nb_groups;
} OptionGroupList; } OptionGroupList;
typedef struct OptionParseContext { typedef struct OptionParseContext {
OptionGroup global_opts; OptionGroup global_opts;
OptionGroupList *groups; OptionGroupList *groups;
int nb_groups; int nb_groups;
/* parsing state */ /* parsing state */
OptionGroup cur_group; OptionGroup cur_group;
@ -360,8 +331,8 @@ int parse_optgroup(void *optctx, OptionGroup *g);
* same as the order of group definitions. * same as the order of group definitions.
*/ */
int split_commandline(OptionParseContext *octx, int argc, char *argv[], int split_commandline(OptionParseContext *octx, int argc, char *argv[],
const OptionDef *options, const OptionDef *options, const OptionGroupDef *groups,
const OptionGroupDef *groups, int nb_groups); int nb_groups);
/** /**
* Free all allocated memory in an OptionParseContext. * 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 st A stream from s for which the options should be filtered.
* @param codec The particular codec 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. * 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, int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, const AVCodec *codec); AVFormatContext *s, AVStream *st, const AVCodec *codec,
AVDictionary **dst);
/** /**
* Setup AVCodecContext options for avformat_find_stream_info(). * 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. * contained in s.
* Each dictionary will contain the options from codec_opts which can * Each dictionary will contain the options from codec_opts which can
* be applied to the corresponding stream codec context. * 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, int setup_find_stream_info_opts(AVFormatContext *s, AVDictionary *codec_opts,
AVDictionary *codec_opts); AVDictionary ***dst);
/** /**
* Print an error message to stderr, indicating filename and a human * Print an error message to stderr, indicating filename and a human
@ -464,53 +434,49 @@ int read_yesno(void);
* preset, may be NULL * preset, may be NULL
*/ */
FILE *get_preset_file(char *filename, size_t filename_size, 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. * 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 elem_size size in bytes of each element
* @param size new element count will be written here * @param size new element count will be written here
* @param new_size number of elements to place in reallocated array * @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 * 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 * a new entry, reallocate the array of pointers and make the new last
* member of this array point to the newly allocated buffer. * member of this array point to the newly allocated buffer.
* Calls exit() on failure.
* *
* @param array array of pointers to reallocate * @param array array of pointers to reallocate
* @param elem_size size of the new element to allocate * @param elem_size size of the new element to allocate
* @param nb_elems pointer to the number of elements of the array array; * @param nb_elems pointer to the number of elements of the array array;
* *nb_elems will be incremented by one by this function. * *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); void *allocate_array_elem(void *array, size_t elem_size, int *nb_elems);
#define GROW_ARRAY(array, nb_elems)\ #define GROW_ARRAY(array, nb_elems) \
array = grow_array(array, sizeof(*array), &nb_elems, nb_elems + 1) grow_array((void **)&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 GET_PIX_FMT_NAME(pix_fmt)\ #define GET_PIX_FMT_NAME(pix_fmt) \
const char *name = av_get_pix_fmt_name(pix_fmt); const char *name = av_get_pix_fmt_name(pix_fmt);
#define GET_CODEC_NAME(id)\ #define GET_CODEC_NAME(id) const char *name = avcodec_descriptor_get(id)->name;
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) const char *name = av_get_sample_fmt_name(sample_fmt)
#define GET_SAMPLE_RATE_NAME(rate)\ #define GET_SAMPLE_RATE_NAME(rate) \
char name[16];\ char name[16]; \
snprintf(name, sizeof(name), "%d", rate); snprintf(name, sizeof(name), "%d", rate);
double get_rotation(int32_t *displaymatrix); double get_rotation(const int32_t *displaymatrix);
#endif /* FFTOOLS_CMDUTILS_H */ #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) 2018-2019 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD * Copyright (c) 2023-2024 ARTHENICA LTD
* *
* This file is part of FFmpeg. * 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 * This file is the modified version of ffmpeg_hw.c file living in ffmpeg source
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * code under the fftools folder. We manually update it each time we depend on a
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries. * 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 * ffmpeg-kit changes by ARTHENICA LTD
* *
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
@ -34,7 +39,8 @@
* *
* 12.2019 * 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 * 08.2018
* -------------------------------------------------------- * --------------------------------------------------------
@ -43,17 +49,16 @@
#include <string.h> #include <string.h>
#include "libavfilter/buffersink.h"
#include "libavutil/avstring.h" #include "libavutil/avstring.h"
#include "libavutil/pixdesc.h" #include "libavutil/pixdesc.h"
#include "libavfilter/buffersink.h"
#include "fftools_ffmpeg.h" #include "fftools_ffmpeg.h"
__thread int nb_hw_devices; __thread int nb_hw_devices;
__thread HWDevice **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; HWDevice *found = NULL;
int i; int i;
for (i = 0; i < nb_hw_devices; 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; return found;
} }
HWDevice *hw_device_get_by_name(const char *name) HWDevice *hw_device_get_by_name(const char *name) {
{
int i; int i;
for (i = 0; i < nb_hw_devices; i++) { for (i = 0; i < nb_hw_devices; i++) {
if (!strcmp(hw_devices[i]->name, name)) if (!strcmp(hw_devices[i]->name, name))
@ -76,11 +80,10 @@ HWDevice *hw_device_get_by_name(const char *name)
return NULL; return NULL;
} }
static HWDevice *hw_device_add(void) static HWDevice *hw_device_add(void) {
{
int err; int err;
err = av_reallocp_array(&hw_devices, nb_hw_devices + 1, err =
sizeof(*hw_devices)); av_reallocp_array(&hw_devices, nb_hw_devices + 1, sizeof(*hw_devices));
if (err) { if (err) {
nb_hw_devices = 0; nb_hw_devices = 0;
return NULL; return NULL;
@ -91,8 +94,7 @@ static HWDevice *hw_device_add(void)
return hw_devices[nb_hw_devices++]; 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 // Make an automatic name of the form "type%d". We arbitrarily
// limit at 1000 anonymous devices of the same type - there is // limit at 1000 anonymous devices of the same type - there is
// probably something else very wrong if you get to this limit. // 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; 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"
// "type=name,key=value,key2=value2" // "type=name,key=value,key2=value2"
// "type=name:device,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) { if (!*p) {
// New device with no parameters. // New device with no parameters.
err = av_hwdevice_ctx_create(&device_ref, type, err = av_hwdevice_ctx_create(&device_ref, type, NULL, NULL, 0);
NULL, NULL, 0);
if (err < 0) if (err < 0)
goto fail; 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, err = av_hwdevice_ctx_create(&device_ref, type,
q ? device : p[0] ? p : NULL, q ? device
: p[0] ? p
: NULL,
options, 0); options, 0);
if (err < 0) if (err < 0)
goto fail; goto fail;
@ -213,8 +215,8 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
goto invalid; goto invalid;
} }
err = av_hwdevice_ctx_create_derived(&device_ref, type, err = av_hwdevice_ctx_create_derived(&device_ref, type, src->device_ref,
src->device_ref, 0); 0);
if (err < 0) if (err < 0)
goto fail; goto fail;
} else if (*p == ',') { } else if (*p == ',') {
@ -225,8 +227,7 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
goto invalid; goto invalid;
} }
err = av_hwdevice_ctx_create(&device_ref, type, err = av_hwdevice_ctx_create(&device_ref, type, NULL, options, 0);
NULL, options, 0);
if (err < 0) if (err < 0)
goto fail; goto fail;
} else { } else {
@ -256,21 +257,18 @@ done:
av_dict_free(&options); av_dict_free(&options);
return err; return err;
invalid: invalid:
av_log(NULL, AV_LOG_ERROR, av_log(NULL, AV_LOG_ERROR, "Invalid device specification \"%s\": %s\n", arg,
"Invalid device specification \"%s\": %s\n", arg, errmsg); errmsg);
err = AVERROR(EINVAL); err = AVERROR(EINVAL);
goto done; goto done;
fail: fail:
av_log(NULL, AV_LOG_ERROR, av_log(NULL, AV_LOG_ERROR, "Device creation failed: %d.\n", err);
"Device creation failed: %d.\n", err);
av_buffer_unref(&device_ref); av_buffer_unref(&device_ref);
goto done; goto done;
} }
static int hw_device_init_from_type(enum AVHWDeviceType type, int hw_device_init_from_type(enum AVHWDeviceType type, const char *device,
const char *device, HWDevice **dev_out) {
HWDevice **dev_out)
{
AVBufferRef *device_ref = NULL; AVBufferRef *device_ref = NULL;
HWDevice *dev; HWDevice *dev;
char *name; 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); err = av_hwdevice_ctx_create(&device_ref, type, device, NULL, 0);
if (err < 0) { if (err < 0) {
av_log(NULL, AV_LOG_ERROR, av_log(NULL, AV_LOG_ERROR, "Device creation failed: %d.\n", err);
"Device creation failed: %d.\n", err);
goto fail; goto fail;
} }
@ -310,8 +307,7 @@ fail:
return err; return err;
} }
void hw_device_free_all(void) void hw_device_free_all(void) {
{
int i; int i;
for (i = 0; i < nb_hw_devices; i++) { for (i = 0; i < nb_hw_devices; i++) {
av_freep(&hw_devices[i]->name); av_freep(&hw_devices[i]->name);
@ -322,208 +318,7 @@ void hw_device_free_all(void)
nb_hw_devices = 0; nb_hw_devices = 0;
} }
static HWDevice *hw_device_match_by_codec(const AVCodec *codec) int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) {
{
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)
{
InputStream *ist = avctx->opaque; InputStream *ist = avctx->opaque;
AVFrame *output = NULL; AVFrame *output = NULL;
enum AVPixelFormat output_format = ist->hwaccel_output_format; 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); err = av_hwframe_transfer_data(output, input, 0);
if (err < 0) { if (err < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to transfer data to " av_log(avctx, AV_LOG_ERROR,
"output frame: %d.\n", err); "Failed to transfer data to "
"output frame: %d.\n",
err);
goto fail; goto fail;
} }
@ -564,45 +361,26 @@ fail:
return err; return err;
} }
int hwaccel_decode_init(AVCodecContext *avctx) AVBufferRef *hw_device_for_filter(void) {
{
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;
// Pick the last hardware device if the user doesn't pick the device for // Pick the last hardware device if the user doesn't pick the device for
// filters explicitly with the filter_hw_device option. // filters explicitly with the filter_hw_device option.
if (filter_hw_device) if (filter_hw_device)
dev = filter_hw_device; return filter_hw_device->device_ref;
else if (nb_hw_devices > 0) { 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) if (nb_hw_devices > 1)
av_log(NULL, AV_LOG_WARNING, "There are %d hardware devices. device " av_log(
"%s of type %s is picked for filters by default. Set hardware " NULL, AV_LOG_WARNING,
"device explicitly with the filter_hw_device option if device " "There are %d hardware devices. device "
"%s is not usable for filters.\n", "%s of type %s is picked for filters by default. Set hardware "
nb_hw_devices, dev->name, "device explicitly with the filter_hw_device option if device "
av_hwdevice_get_type_name(dev->type), dev->name); "%s is not usable for filters.\n",
} else nb_hw_devices, dev->name, av_hwdevice_get_type_name(dev->type),
dev = NULL; dev->name);
if (dev) { return dev->device_ref;
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);
}
} }
return 0; return NULL;
} }

@ -1,7 +1,7 @@
/* /*
* This file is part of FFmpeg. * This file is part of FFmpeg.
* Copyright (c) 2022 Taner Sener * 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 * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * 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 * This file is the modified version of ffmpeg_mux.c file living in ffmpeg
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * 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. * by us to develop the ffmpeg-kit library.
* *
* ffmpeg-kit changes by ARTHENICA LTD * 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 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
@ -55,44 +61,38 @@
#include "libavutil/intreadwrite.h" #include "libavutil/intreadwrite.h"
#include "libavutil/log.h" #include "libavutil/log.h"
#include "libavutil/mem.h" #include "libavutil/mem.h"
#include "libavutil/timestamp.h"
#include "libavutil/thread.h" #include "libavutil/thread.h"
#include "libavutil/timestamp.h"
#include "libavcodec/packet.h" #include "libavcodec/packet.h"
#include "libavformat/avformat.h" #include "libavformat/avformat.h"
#include "libavformat/avio.h" #include "libavformat/avio.h"
#include "ffmpeg_context.h"
__thread int want_sdp = 1; __thread int want_sdp = 1;
MuxStream *ms_from_ost(OutputStream *ost) MuxStream *ms_from_ost(OutputStream *ost) { return (MuxStream *)ost; }
{
return (MuxStream*)ost;
}
static Muxer *mux_from_of(OutputFile *of) static Muxer *mux_from_of(OutputFile *of) { return (Muxer *)of; }
{
return (Muxer*)of;
}
static int64_t filesize(AVIOContext *pb) static int64_t filesize(AVIOContext *pb) {
{
int64_t ret = -1; int64_t ret = -1;
if (pb) { if (pb) {
ret = avio_size(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); ret = avio_tell(pb);
} }
return ret; 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); MuxStream *ms = ms_from_ost(ost);
AVFormatContext *s = mux->fc; AVFormatContext *s = mux->fc;
AVStream *st = ost->st;
int64_t fs; int64_t fs;
uint64_t frame_num; uint64_t frame_num;
int ret; int ret;
@ -104,50 +104,68 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
goto fail; 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; pkt->pts = pkt->dts = AV_NOPTS_VALUE;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { // rescale timestamps to the stream timebase
if (ost->frame_rate.num && ost->is_cfr) { if (ost->type == AVMEDIA_TYPE_AUDIO && !ost->enc) {
if (pkt->duration > 0) // use av_rescale_delta() for streamcopying audio, to preserve
av_log(ost, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n"); // accuracy with coarse input timebases
pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate), int duration =
pkt->time_base); av_get_audio_frame_duration2(ost->st->codecpar, pkt->size);
}
} if (!duration)
duration = ost->st->codecpar->frame_size;
av_packet_rescale_ts(pkt, pkt->time_base, ost->st->time_base);
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; pkt->time_base = ost->st->time_base;
if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) { if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {
if (pkt->dts != AV_NOPTS_VALUE && if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE &&
pkt->pts != AV_NOPTS_VALUE &&
pkt->dts > pkt->pts) { pkt->dts > pkt->pts) {
av_log(s, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64" in output stream %d:%d, replacing by guess\n", av_log(s, AV_LOG_WARNING,
pkt->dts, pkt->pts, "Invalid DTS: %" PRId64 " PTS: %" PRId64
ost->file_index, ost->st->index); " in output stream %d:%d, replacing by guess\n",
pkt->pts = pkt->dts, pkt->pts, ost->file_index, ost->st->index);
pkt->dts = pkt->pts + pkt->dts + ms->last_mux_dts + 1 pkt->pts = pkt->dts =
- FFMIN3(pkt->pts, pkt->dts, ms->last_mux_dts + 1) pkt->pts + pkt->dts + ms->last_mux_dts + 1 -
- FFMAX3(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) && if ((ost->type == AVMEDIA_TYPE_AUDIO ||
pkt->dts != AV_NOPTS_VALUE && ost->type == AVMEDIA_TYPE_VIDEO ||
ms->last_mux_dts != AV_NOPTS_VALUE) { ost->type == AVMEDIA_TYPE_SUBTITLE) &&
int64_t max = ms->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT); 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) { 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) if (exit_on_error)
loglevel = AV_LOG_ERROR; loglevel = AV_LOG_ERROR;
av_log(s, loglevel, "Non-monotonous DTS in output stream " av_log(s, loglevel,
"%d:%d; previous: %"PRId64", current: %"PRId64"; ", "Non-monotonic DTS in output stream "
ost->file_index, ost->st->index, ms->last_mux_dts, pkt->dts); "%d:%d; previous: %" PRId64 ", current: %" PRId64 "; ",
ost->file_index, ost->st->index, ms->last_mux_dts,
pkt->dts);
if (exit_on_error) { if (exit_on_error) {
ret = AVERROR(EINVAL); ret = AVERROR(EINVAL);
goto fail; 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", "in incorrect timestamps in the output file.\n",
max); max);
if (pkt->pts >= pkt->dts) 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; 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); frame_num = atomic_fetch_add(&ost->packets_written, 1);
pkt->stream_index = ost->index; pkt->stream_index = ost->index;
if (debug_ts) { if (debug_ts) {
av_log(ost, AV_LOG_INFO, "muxer <- type:%s " av_log(
"pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d\n", ost, AV_LOG_INFO,
av_get_media_type_string(st->codecpar->codec_type), "muxer <- type:%s "
av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base), "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s "
av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base), "duration_time:%s size:%d\n",
av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base), av_get_media_type_string(ost->type), av_ts2str(pkt->pts),
pkt->size 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) 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); ret = av_interleaved_write_frame(s, pkt);
if (ret < 0) { 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; goto fail;
} }
@ -189,8 +210,8 @@ fail:
return ret; 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; OutputFile *of = &mux->of;
if (ost->sq_idx_mux >= 0) { if (ost->sq_idx_mux >= 0) {
@ -204,11 +225,16 @@ static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int
while (1) { while (1) {
ret = sq_receive(mux->sq_mux, -1, SQPKT(mux->sq_pkt)); ret = sq_receive(mux->sq_mux, -1, SQPKT(mux->sq_pkt));
if (ret < 0) if (ret < 0) {
return (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) ? 0 : ret; /* 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], ret = write_packet(mux, of->streams[ret], mux->sq_pkt);
mux->sq_pkt);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
@ -218,19 +244,21 @@ static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int
return 0; return 0;
} }
static void thread_set_name(OutputFile *of) static void thread_set_name(OutputFile *of) {
{
char name[16]; char name[16];
snprintf(name, sizeof(name), "mux%d:%s", of->index, of->format->name); snprintf(name, sizeof(name), "mux%d:%s", of->index, of->format->name);
ff_thread_setname(name); ff_thread_setname(name);
} }
static void *muxer_thread(void *arg) static void *muxer_thread(void *arg) {
{ FFmpegContext *context = (FFmpegContext *)arg;
Muxer *mux = arg; loadFFmpegContext(context);
Muxer *mux = (Muxer *)context->arg;
av_free(arg);
OutputFile *of = &mux->of; OutputFile *of = &mux->of;
AVPacket *pkt = NULL; AVPacket *pkt = NULL;
int ret = 0; int ret = 0;
pkt = av_packet_alloc(); pkt = av_packet_alloc();
if (!pkt) { if (!pkt) {
@ -254,9 +282,15 @@ static void *muxer_thread(void *arg)
ost = of->streams[stream_idx]; ost = of->streams[stream_idx];
ret = sync_queue_process(mux, ost, ret < 0 ? NULL : pkt, &stream_eof); ret = sync_queue_process(mux, ost, ret < 0 ? NULL : pkt, &stream_eof);
av_packet_unref(pkt); av_packet_unref(pkt);
if (ret == AVERROR_EOF && stream_eof) if (ret == AVERROR_EOF) {
tq_receive_finish(mux->tq, stream_idx); if (stream_eof) {
else if (ret < 0) { 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"); av_log(mux, AV_LOG_ERROR, "Error muxing a packet\n");
break; break;
} }
@ -270,11 +304,10 @@ finish:
av_log(mux, AV_LOG_VERBOSE, "Terminating muxer thread\n"); 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; int ret = 0;
if (!pkt || ost->finished & MUXER_FINISHED) if (!pkt || ost->finished & MUXER_FINISHED)
@ -295,8 +328,7 @@ finish:
return ret == AVERROR_EOF ? 0 : ret; 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); MuxStream *ms = ms_from_ost(ost);
AVPacket *tmp_pkt = NULL; AVPacket *tmp_pkt = NULL;
int ret; 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 cur_size = av_fifo_can_read(ms->muxing_queue);
size_t pkt_size = pkt ? pkt->size : 0; size_t pkt_size = pkt ? pkt->size : 0;
unsigned int are_we_over_size = unsigned int are_we_over_size =
(ms->muxing_queue_data_size + pkt_size) > ms->muxing_queue_data_threshold; (ms->muxing_queue_data_size + pkt_size) >
size_t limit = are_we_over_size ? ms->max_muxing_queue_size : SIZE_MAX; 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); size_t new_size = FFMIN(2 * cur_size, limit);
if (new_size <= cur_size) { if (new_size <= cur_size) {
@ -337,15 +370,14 @@ static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
return 0; return 0;
} }
static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost) static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost) {
{
int ret; int ret;
if (mux->tq) { if (mux->tq) {
return thread_submit_packet(mux, ost, pkt); return thread_submit_packet(mux, ost, pkt);
} else { } else {
/* the muxer is not initialized yet, buffer the packet */ /* the muxer is not initialized yet, buffer the packet */
ret = queue_packet(mux, ost, pkt); ret = queue_packet(ost, pkt);
if (ret < 0) { if (ret < 0) {
if (pkt) if (pkt)
av_packet_unref(pkt); av_packet_unref(pkt);
@ -356,30 +388,34 @@ static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost)
return 0; 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); Muxer *mux = mux_from_of(of);
MuxStream *ms = ms_from_ost(ost); MuxStream *ms = ms_from_ost(ost);
const char *err_msg; const char *err_msg;
int ret = 0; int ret = 0;
if (!eof && pkt->dts != AV_NOPTS_VALUE) if (pkt && pkt->dts != AV_NOPTS_VALUE)
ost->last_mux_dts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q); ost->last_mux_dts =
av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q);
/* apply the output bitstream filters */ /* apply the output bitstream filters */
if (ms->bsf_ctx) { if (ms->bsf_ctx) {
int bsf_eof = 0; 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) { if (ret < 0) {
err_msg = "submitting a packet for bitstream filtering"; err_msg = "submitting a packet for bitstream filtering";
goto fail; goto fail;
} }
while (!bsf_eof) { 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)) if (ret == AVERROR(EAGAIN))
return; return 0;
else if (ret == AVERROR_EOF) else if (ret == AVERROR_EOF)
bsf_eof = 1; bsf_eof = 1;
else if (ret < 0) { else if (ret < 0) {
@ -387,30 +423,100 @@ void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof)
goto fail; 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) if (ret < 0)
goto mux_fail; goto mux_fail;
} }
} else { } else {
ret = submit_packet(mux, eof ? NULL : pkt, ost); ret = submit_packet(mux, pkt, ost);
if (ret < 0) if (ret < 0)
goto mux_fail; goto mux_fail;
} }
return; return 0;
mux_fail: mux_fail:
err_msg = "submitting a packet to the muxer"; err_msg = "submitting a packet to the muxer";
fail: fail:
av_log(ost, AV_LOG_ERROR, "Error %s\n", err_msg); av_log(ost, AV_LOG_ERROR, "Error %s\n", err_msg);
if (exit_on_error) return exit_on_error ? ret : 0;
exit_program(1); }
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; void *ret;
if (!mux || !mux->tq) if (!mux || !mux->tq)
@ -426,13 +532,7 @@ static int thread_stop(Muxer *mux)
return (int)(intptr_t)ret; return (int)(intptr_t)ret;
} }
static void pkt_move(void *dst, void *src) static int thread_start(Muxer *mux) {
{
av_packet_move_ref(dst, src);
}
static int thread_start(Muxer *mux)
{
AVFormatContext *fc = mux->fc; AVFormatContext *fc = mux->fc;
ObjPool *op; ObjPool *op;
int ret; int ret;
@ -447,7 +547,10 @@ static int thread_start(Muxer *mux)
return AVERROR(ENOMEM); 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) { if (ret) {
tq_free(&mux->tq); tq_free(&mux->tq);
return AVERROR(ret); return AVERROR(ret);
@ -456,13 +559,9 @@ static int thread_start(Muxer *mux)
/* flush the muxing queues */ /* flush the muxing queues */
for (int i = 0; i < fc->nb_streams; i++) { for (int i = 0; i < fc->nb_streams; i++) {
OutputStream *ost = mux->of.streams[i]; OutputStream *ost = mux->of.streams[i];
MuxStream *ms = ms_from_ost(ost); MuxStream *ms = ms_from_ost(ost);
AVPacket *pkt; 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) { while (av_fifo_read(ms->muxing_queue, &pkt, 1) >= 0) {
ret = thread_submit_packet(mux, ost, pkt); ret = thread_submit_packet(mux, ost, pkt);
if (pkt) { if (pkt) {
@ -477,8 +576,7 @@ static int thread_start(Muxer *mux)
return 0; return 0;
} }
static int print_sdp(void) static int print_sdp(void) {
{
char sdp[16384]; char sdp[16384];
int i; int i;
int j, ret; int j, ret;
@ -516,7 +614,8 @@ static int print_sdp(void)
} else { } else {
ret = avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL); ret = avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL);
if (ret < 0) { 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; goto fail;
} }
@ -533,8 +632,7 @@ fail:
return ret; return ret;
} }
int mux_check_init(Muxer *mux) int mux_check_init(Muxer *mux) {
{
OutputFile *of = &mux->of; OutputFile *of = &mux->of;
AVFormatContext *fc = mux->fc; AVFormatContext *fc = mux->fc;
int ret, i; int ret, i;
@ -547,11 +645,13 @@ int mux_check_init(Muxer *mux)
ret = avformat_write_header(fc, &mux->opts); ret = avformat_write_header(fc, &mux->opts);
if (ret < 0) { if (ret < 0) {
av_log(mux, AV_LOG_ERROR, "Could not write header (incorrect codec " av_log(mux, AV_LOG_ERROR,
"parameters ?): %s\n", av_err2str(ret)); "Could not write header (incorrect codec "
"parameters ?): %s\n",
av_err2str(ret));
return ret; return ret;
} }
//assert_avoptions(of->opts); // assert_avoptions(of->opts);
mux->header_written = 1; mux->header_written = 1;
av_dump_format(fc, of->index, fc->url, 1); av_dump_format(fc, of->index, fc->url, 1);
@ -580,16 +680,15 @@ int mux_check_init(Muxer *mux)
return 0; return 0;
} }
static int bsf_init(MuxStream *ms) static int bsf_init(MuxStream *ms) {
{
OutputStream *ost = &ms->ost; OutputStream *ost = &ms->ost;
AVBSFContext *ctx = ms->bsf_ctx; AVBSFContext *ctx = ms->bsf_ctx;
int ret; int ret;
if (!ctx) 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) if (ret < 0)
return ret; return ret;
@ -607,18 +706,18 @@ static int bsf_init(MuxStream *ms)
return ret; return ret;
ost->st->time_base = ctx->time_base_out; ost->st->time_base = ctx->time_base_out;
ms->bsf_pkt = av_packet_alloc();
if (!ms->bsf_pkt)
return AVERROR(ENOMEM);
return 0; return 0;
} }
int of_stream_init(OutputFile *of, OutputStream *ost) int of_stream_init(OutputFile *of, OutputStream *ost) {
{
Muxer *mux = mux_from_of(of); Muxer *mux = mux_from_of(of);
MuxStream *ms = ms_from_ost(ost); MuxStream *ms = ms_from_ost(ost);
int ret; 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 /* initialize bitstream filters for the output stream
* needs to be done here, because the codec id for streamcopy is not * needs to be done here, because the codec id for streamcopy is not
* known until now */ * known until now */
@ -626,16 +725,130 @@ int of_stream_init(OutputFile *of, OutputStream *ost)
if (ret < 0) if (ret < 0)
return ret; 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; ost->initialized = 1;
return mux_check_init(mux); 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); Muxer *mux = mux_from_of(of);
AVFormatContext *fc = mux->fc; AVFormatContext *fc = mux->fc;
int ret; int ret, mux_result = 0;
if (!mux->tq) { if (!mux->tq) {
av_log(mux, AV_LOG_ERROR, av_log(mux, AV_LOG_ERROR,
@ -644,14 +857,13 @@ int of_write_trailer(OutputFile *of)
return AVERROR(EINVAL); return AVERROR(EINVAL);
} }
ret = thread_stop(mux); mux_result = thread_stop(mux);
if (ret < 0)
main_ffmpeg_return_code = ret;
ret = av_write_trailer(fc); ret = av_write_trailer(fc);
if (ret < 0) { if (ret < 0) {
av_log(mux, AV_LOG_ERROR, "Error writing trailer: %s\n", av_err2str(ret)); av_log(mux, AV_LOG_ERROR, "Error writing trailer: %s\n",
return ret; av_err2str(ret));
mux_result = err_merge(mux_result, ret);
} }
mux->last_filesize = filesize(fc->pb); mux->last_filesize = filesize(fc->pb);
@ -659,16 +871,22 @@ int of_write_trailer(OutputFile *of)
if (!(of->format->flags & AVFMT_NOFILE)) { if (!(of->format->flags & AVFMT_NOFILE)) {
ret = avio_closep(&fc->pb); ret = avio_closep(&fc->pb);
if (ret < 0) { if (ret < 0) {
av_log(mux, AV_LOG_ERROR, "Error closing file: %s\n", av_err2str(ret)); av_log(mux, AV_LOG_ERROR, "Error closing file: %s\n",
return ret; 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; OutputStream *ost = *post;
MuxStream *ms; MuxStream *ms;
@ -676,6 +894,8 @@ static void ost_free(OutputStream **post)
return; return;
ms = ms_from_ost(ost); ms = ms_from_ost(ost);
enc_free(&ost->enc);
if (ost->logfile) { if (ost->logfile) {
if (fclose(ost->logfile)) if (fclose(ost->logfile))
av_log(ms, AV_LOG_ERROR, av_log(ms, AV_LOG_ERROR,
@ -691,18 +911,17 @@ static void ost_free(OutputStream **post)
av_fifo_freep2(&ms->muxing_queue); av_fifo_freep2(&ms->muxing_queue);
} }
avcodec_parameters_free(&ost->par_in);
av_bsf_free(&ms->bsf_ctx); av_bsf_free(&ms->bsf_ctx);
av_packet_free(&ms->bsf_pkt);
av_frame_free(&ost->filtered_frame); av_packet_free(&ms->pkt);
av_frame_free(&ost->sq_frame);
av_frame_free(&ost->last_frame);
av_packet_free(&ost->pkt);
av_dict_free(&ost->encoder_opts); av_dict_free(&ost->encoder_opts);
av_freep(&ost->kf.pts); av_freep(&ost->kf.pts);
av_expr_free(ost->kf.pexpr); av_expr_free(ost->kf.pexpr);
av_freep(&ost->avfilter);
av_freep(&ost->logfile_prefix); av_freep(&ost->logfile_prefix);
av_freep(&ost->apad); av_freep(&ost->apad);
@ -733,8 +952,7 @@ static void ost_free(OutputStream **post)
av_freep(post); av_freep(post);
} }
static void fc_close(AVFormatContext **pfc) static void fc_close(AVFormatContext **pfc) {
{
AVFormatContext *fc = *pfc; AVFormatContext *fc = *pfc;
if (!fc) if (!fc)
@ -747,8 +965,7 @@ static void fc_close(AVFormatContext **pfc)
*pfc = NULL; *pfc = NULL;
} }
void of_close(OutputFile **pof) void of_free(OutputFile **pof) {
{
OutputFile *of = *pof; OutputFile *of = *pof;
Muxer *mux; Muxer *mux;
@ -774,8 +991,7 @@ void of_close(OutputFile **pof)
av_freep(pof); av_freep(pof);
} }
int64_t of_filesize(OutputFile *of) int64_t of_filesize(OutputFile *of) {
{
Muxer *mux = mux_from_of(of); Muxer *mux = mux_from_of(of);
return atomic_load(&mux->last_filesize); return atomic_load(&mux->last_filesize);
} }

@ -1,6 +1,6 @@
/* /*
* Muxer internal APIs - should not be included outside of ffmpeg_mux* * 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. * 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 * This file is the modified version of ffmpeg_mux.h file living in ffmpeg
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * 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. * by us to develop ffmpeg-kit library.
* *
* ffmpeg-kit changes by ARTHENICA LTD * ffmpeg-kit changes by ARTHENICA LTD
* *
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
* - fftools header names updated * - fftools header names updated
* - want_sdp made thread-local * - want_sdp made thread-local
* - EncStatsFile declaration migrated from ffmpeg_mux_init.c * - 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 * - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT,
* ffmpeg.h * SPECIFIER_OPT_FMT declarations migrated from ffmpeg.h
* - ms_from_ost migrated to ffmpeg_mux.c * - ms_from_ost migrated to ffmpeg_mux.c
*/ */
@ -53,49 +58,58 @@
#include "libavutil/fifo.h" #include "libavutil/fifo.h"
#include "libavutil/thread.h" #include "libavutil/thread.h"
#define SPECIFIER_OPT_FMT_str "%s" #define SPECIFIER_OPT_FMT_str "%s"
#define SPECIFIER_OPT_FMT_i "%i" #define SPECIFIER_OPT_FMT_i "%i"
#define SPECIFIER_OPT_FMT_i64 "%"PRId64 #define SPECIFIER_OPT_FMT_i64 "%" PRId64
#define SPECIFIER_OPT_FMT_ui64 "%"PRIu64 #define SPECIFIER_OPT_FMT_ui64 "%" PRIu64
#define SPECIFIER_OPT_FMT_f "%f" #define SPECIFIER_OPT_FMT_f "%f"
#define SPECIFIER_OPT_FMT_dbl "%lf" #define SPECIFIER_OPT_FMT_dbl "%lf"
#define WARN_MULTIPLE_OPT_USAGE(name, type, so, st)\ #define WARN_MULTIPLE_OPT_USAGE(name, type, so, st) \
{\ { \
char namestr[128] = "";\ char namestr[128] = ""; \
const char *spec = so->specifier && so->specifier[0] ? so->specifier : "";\ const char *spec = \
for (int _i = 0; opt_name_##name[_i]; _i++)\ so->specifier && so->specifier[0] ? so->specifier : ""; \
av_strlcatf(namestr, sizeof(namestr), "-%s%s", opt_name_##name[_i], opt_name_##name[_i+1] ? (opt_name_##name[_i+2] ? ", " : " or ") : "");\ for (int _i = 0; opt_name_##name[_i]; _i++) \
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",\ av_strlcatf(namestr, sizeof(namestr), "-%s%s", \
namestr, st->index, opt_name_##name[0], spec[0] ? ":" : "", spec, so->u.type);\ opt_name_##name[_i], \
} opt_name_##name[_i + 1] \
? (opt_name_##name[_i + 2] ? ", " : " or ") \
#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\ : ""); \
{\ av_log(NULL, AV_LOG_WARNING, \
int _ret, _matches = 0;\ "Multiple %s options specified for stream %d, only the last " \
SpecifierOpt *so;\ "option '-%s%s%s " SPECIFIER_OPT_FMT_##type \
for (int _i = 0; _i < o->nb_ ## name; _i++) {\ "' will be used.\n", \
char *spec = o->name[_i].specifier;\ namestr, st->index, opt_name_##name[0], spec[0] ? ":" : "", \
if ((_ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\ spec, so->u.type); \
outvar = o->name[_i].u.type;\ }
so = &o->name[_i];\
_matches++;\ #define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st) \
} else if (_ret < 0)\ { \
exit_program(1);\ int _ret, _matches = 0; \
}\ SpecifierOpt *so; \
if (_matches > 1)\ for (int _i = 0; _i < o->nb_##name; _i++) { \
WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\ char *spec = o->name[_i].specifier; \
} if ((_ret = check_stream_specifier(fmtctx, st, spec)) > 0) { \
outvar = o->name[_i].u.type; \
#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\ so = &o->name[_i]; \
{\ _matches++; \
int i;\ } else if (_ret < 0) \
for (i = 0; i < o->nb_ ## name; i++) {\ return _ret; \
char *spec = o->name[i].specifier;\ } \
if (!strcmp(spec, mediatype))\ if (_matches > 1) \
outvar = o->name[i].u.type;\ 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 { typedef struct MuxStream {
OutputStream ost; OutputStream ost;
@ -103,10 +117,14 @@ typedef struct MuxStream {
// name used for logging // name used for logging
char log_name[32]; 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; AVFifo *muxing_queue;
AVBSFContext *bsf_ctx; AVBSFContext *bsf_ctx;
AVPacket *bsf_pkt;
AVPacket *pkt;
EncStats stats; EncStats stats;
@ -123,9 +141,27 @@ typedef struct MuxStream {
/* Threshold after which max_muxing_queue_size will be in effect */ /* Threshold after which max_muxing_queue_size will be in effect */
size_t muxing_queue_data_threshold; 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 /* dts of the last packet sent to the muxer, in the stream timebase
* used for making up missing dts values */ * used for making up missing dts values */
int64_t last_mux_dts; 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; } MuxStream;
typedef struct Muxer { typedef struct Muxer {
@ -136,7 +172,7 @@ typedef struct Muxer {
AVFormatContext *fc; AVFormatContext *fc;
pthread_t thread; pthread_t thread;
ThreadQueue *tq; ThreadQueue *tq;
AVDictionary *opts; AVDictionary *opts;
@ -152,11 +188,6 @@ typedef struct Muxer {
AVPacket *sq_pkt; AVPacket *sq_pkt;
} Muxer; } Muxer;
typedef struct EncStatsFile {
char *path;
AVIOContext *io;
} EncStatsFile;
/* whether we want to print an SDP, set in of_open() */ /* whether we want to print an SDP, set in of_open() */
extern __thread int want_sdp; 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 * This file is the modified version of fopen_utf8.h file living in ffmpeg
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * 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. * by us to develop the ffmpeg-kit library.
* *
* ffmpeg-kit changes by Taner Sener * ffmpeg-kit changes by Taner Sener
@ -37,13 +38,13 @@
#ifdef _WIN32 #ifdef _WIN32
#include "libavutil/wchar_filename.h" #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; wchar_t *path_w, *mode_w;
FILE *f; FILE *f;
/* convert UTF-8 to wide chars */ /* 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; return NULL;
if (!path_w) if (!path_w)
goto fallback; goto fallback;
@ -70,8 +71,7 @@ fallback:
#else #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); return fopen(path, mode);
} }
#endif #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 * This file is the modified version of objpool.c file living in ffmpeg source
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * code under the fftools folder. We manually update it each time we depend on a
* by us to develop ffmpeg-kit library. * 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 * ffmpeg-kit changes by ARTHENICA LTD
* *
@ -42,17 +43,16 @@
#include "fftools_objpool.h" #include "fftools_objpool.h"
struct ObjPool { struct ObjPool {
void *pool[32]; void *pool[32];
unsigned int pool_count; unsigned int pool_count;
ObjPoolCBAlloc alloc; ObjPoolCBAlloc alloc;
ObjPoolCBReset reset; ObjPoolCBReset reset;
ObjPoolCBFree free; ObjPoolCBFree free;
}; };
ObjPool *objpool_alloc(ObjPoolCBAlloc cb_alloc, ObjPoolCBReset cb_reset, ObjPool *objpool_alloc(ObjPoolCBAlloc cb_alloc, ObjPoolCBReset cb_reset,
ObjPoolCBFree cb_free) ObjPoolCBFree cb_free) {
{
ObjPool *op = av_mallocz(sizeof(*op)); ObjPool *op = av_mallocz(sizeof(*op));
if (!op) if (!op)
@ -60,13 +60,12 @@ ObjPool *objpool_alloc(ObjPoolCBAlloc cb_alloc, ObjPoolCBReset cb_reset,
op->alloc = cb_alloc; op->alloc = cb_alloc;
op->reset = cb_reset; op->reset = cb_reset;
op->free = cb_free; op->free = cb_free;
return op; return op;
} }
void objpool_free(ObjPool **pop) void objpool_free(ObjPool **pop) {
{
ObjPool *op = *pop; ObjPool *op = *pop;
if (!op) if (!op)
@ -78,8 +77,7 @@ void objpool_free(ObjPool **pop)
av_freep(pop); av_freep(pop);
} }
int objpool_get(ObjPool *op, void **obj) int objpool_get(ObjPool *op, void **obj) {
{
if (op->pool_count) { if (op->pool_count) {
*obj = op->pool[--op->pool_count]; *obj = op->pool[--op->pool_count];
op->pool[op->pool_count] = NULL; op->pool[op->pool_count] = NULL;
@ -89,8 +87,7 @@ int objpool_get(ObjPool *op, void **obj)
return *obj ? 0 : AVERROR(ENOMEM); return *obj ? 0 : AVERROR(ENOMEM);
} }
void objpool_release(ObjPool *op, void **obj) void objpool_release(ObjPool *op, void **obj) {
{
if (!*obj) if (!*obj)
return; return;
@ -104,42 +101,26 @@ void objpool_release(ObjPool *op, void **obj)
*obj = NULL; *obj = NULL;
} }
static void *alloc_packet(void) static void *alloc_packet(void) { return av_packet_alloc(); }
{ static void *alloc_frame(void) { return av_frame_alloc(); }
return av_packet_alloc();
}
static void *alloc_frame(void)
{
return av_frame_alloc();
}
static void reset_packet(void *obj) static void reset_packet(void *obj) { av_packet_unref(obj); }
{ static void reset_frame(void *obj) { av_frame_unref(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; AVPacket *pkt = *obj;
av_packet_free(&pkt); av_packet_free(&pkt);
*obj = NULL; *obj = NULL;
} }
static void free_frame(void **obj) static void free_frame(void **obj) {
{
AVFrame *frame = *obj; AVFrame *frame = *obj;
av_frame_free(&frame); av_frame_free(&frame);
*obj = NULL; *obj = NULL;
} }
ObjPool *objpool_alloc_packets(void) ObjPool *objpool_alloc_packets(void) {
{
return objpool_alloc(alloc_packet, reset_packet, free_packet); 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); 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 * This file is the modified version of objpool.h file living in ffmpeg source
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * code under the fftools folder. We manually update it each time we depend on a
* by us to develop ffmpeg-kit library. * 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 * ffmpeg-kit changes by ARTHENICA LTD
* *
@ -34,17 +35,17 @@
typedef struct ObjPool ObjPool; typedef struct ObjPool ObjPool;
typedef void* (*ObjPoolCBAlloc)(void); typedef void *(*ObjPoolCBAlloc)(void);
typedef void (*ObjPoolCBReset)(void *); typedef void (*ObjPoolCBReset)(void *);
typedef void (*ObjPoolCBFree)(void **); typedef void (*ObjPoolCBFree)(void **);
void objpool_free(ObjPool **op); void objpool_free(ObjPool **op);
ObjPool *objpool_alloc(ObjPoolCBAlloc cb_alloc, ObjPoolCBReset cb_reset, ObjPool *objpool_alloc(ObjPoolCBAlloc cb_alloc, ObjPoolCBReset cb_reset,
ObjPoolCBFree cb_free); ObjPoolCBFree cb_free);
ObjPool *objpool_alloc_packets(void); ObjPool *objpool_alloc_packets(void);
ObjPool *objpool_alloc_frames(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); void objpool_release(ObjPool *op, void **obj);
#endif // FFTOOLS_OBJPOOL_H #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 * This file is the modified version of opt_common.h file living in ffmpeg
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * 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. * by us to develop the ffmpeg-kit library.
* *
* ffmpeg-kit changes by Taner Sener * ffmpeg-kit changes by Taner Sener
* *
* 09.2022 * 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 * - fftools_ prefix added to fftools headers
*/ */

@ -1,6 +1,6 @@
/* /*
* This file is part of FFmpeg. * 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 * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * 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 * This file is the modified version of sync_queue.c file living in ffmpeg
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * 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. * by us to develop ffmpeg-kit library.
* *
* ffmpeg-kit changes by ARTHENICA LTD * ffmpeg-kit changes by ARTHENICA LTD
* *
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
@ -34,31 +39,76 @@
#include <string.h> #include <string.h>
#include "libavutil/avassert.h" #include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/cpu.h"
#include "libavutil/error.h" #include "libavutil/error.h"
#include "libavutil/fifo.h" #include "libavutil/fifo.h"
#include "libavutil/mathematics.h" #include "libavutil/mathematics.h"
#include "libavutil/mem.h" #include "libavutil/mem.h"
#include "libavutil/samplefmt.h"
#include "libavutil/timestamp.h"
#include "fftools_objpool.h" #include "fftools_objpool.h"
#include "fftools_sync_queue.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 { typedef struct SyncQueueStream {
AVFifo *fifo; AVFifo *fifo;
AVRational tb; AVRational tb;
/* number of audio samples in fifo */
uint64_t samples_queued;
/* stream head: largest timestamp seen */ /* stream head: largest timestamp seen */
int64_t head_ts; int64_t head_ts;
int limiting; int limiting;
/* no more frames will be sent for this stream */ /* no more frames will be sent for this stream */
int finished; int finished;
uint64_t frames_sent; uint64_t frames_sent;
uint64_t frames_max; uint64_t samples_sent;
uint64_t frames_max;
int frame_samples;
} SyncQueueStream; } SyncQueueStream;
struct SyncQueue { struct SyncQueue {
enum SyncQueueType type; enum SyncQueueType type;
void *logctx;
/* no more frames will be sent for any stream */ /* no more frames will be sent for any stream */
int finished; int finished;
/* sync head: the stream with the _smallest_ head timestamp /* sync head: the stream with the _smallest_ head timestamp
@ -71,37 +121,76 @@ struct SyncQueue {
int64_t buf_size_us; int64_t buf_size_us;
SyncQueueStream *streams; SyncQueueStream *streams;
unsigned int nb_streams; unsigned int nb_streams;
// pool of preallocated frames to avoid constant allocations // pool of preallocated frames to avoid constant allocations
ObjPool *pool; ObjPool *pool;
int have_limiting;
uintptr_t align_mask;
}; };
static void frame_move(const SyncQueue *sq, SyncQueueFrame dst, static void frame_move(const SyncQueue *sq, SyncQueueFrame dst,
SyncQueueFrame src) SyncQueueFrame src) {
{
if (sq->type == SYNC_QUEUE_PACKETS) if (sq->type == SYNC_QUEUE_PACKETS)
av_packet_move_ref(dst.p, src.p); av_packet_move_ref(dst.p, src.p);
else else
av_frame_move_ref(dst.f, src.f); av_frame_move_ref(dst.f, src.f);
} }
static int64_t frame_ts(const SyncQueue *sq, SyncQueueFrame frame) /**
{ * Compute the end timestamp of a frame. If nb_samples is provided, consider
return (sq->type == SYNC_QUEUE_PACKETS) ? * the frame to have this number of audio samples, otherwise use frame duration.
frame.p->pts + frame.p->duration : */
frame.f->pts + frame.f->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) static int frame_null(const SyncQueue *sq, SyncQueueFrame frame) {
{ return (sq->type == SYNC_QUEUE_PACKETS) ? (frame.p == NULL)
return (sq->type == SYNC_QUEUE_PACKETS) ? (frame.p == NULL) : (frame.f == 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]; 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; st->finished = 1;
if (st->limiting && st->head_ts != AV_NOPTS_VALUE) { 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; sq->head_finished_stream = stream_idx;
} }
/* mark as finished all streams that should no longer receive new frames, /* mark as finished all streams that should no longer receive new
* due to them being ahead of some finished stream */ * frames, due to them being ahead of some finished stream */
st = &sq->streams[sq->head_finished_stream]; st = &sq->streams[sq->head_finished_stream];
for (unsigned int i = 0; i < sq->nb_streams; i++) { for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueStream *st1 = &sq->streams[i]; SyncQueueStream *st1 = &sq->streams[i];
if (st != st1 && st1->head_ts != AV_NOPTS_VALUE && 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; st1->finished = 1;
}
} }
} }
@ -130,36 +226,46 @@ static void finish_stream(SyncQueue *sq, unsigned int stream_idx)
return; return;
} }
sq->finished = 1; 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) { if (sq->head_stream < 0) {
unsigned first_limiting = UINT_MAX;
/* wait for one timestamp in each stream before determining /* wait for one timestamp in each stream before determining
* the queue head */ * the queue head */
for (unsigned int i = 0; i < sq->nb_streams; i++) { for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueStream *st = &sq->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; return;
if (first_limiting == UINT_MAX)
first_limiting = i;
} }
// placeholder value, correct one will be found below // 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++) { 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]; SyncQueueStream *st_other = &sq->streams[i];
if (st_other->limiting && st_other->head_ts != AV_NOPTS_VALUE && if (st_other->limiting && st_other->head_ts != AV_NOPTS_VALUE &&
av_compare_ts(st_other->head_ts, st_other->tb, av_compare_ts(st_other->head_ts, st_other->tb, st_head->head_ts,
st_head->head_ts, st_head->tb) < 0) st_head->tb) < 0)
sq->head_stream = i; sq->head_stream = i;
} }
} }
/* update this stream's head timestamp */ /* 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]; SyncQueueStream *st = &sq->streams[stream_idx];
if (ts == AV_NOPTS_VALUE || 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 */ * this stream is also finished */
if (sq->head_finished_stream >= 0 && if (sq->head_finished_stream >= 0 &&
av_compare_ts(sq->streams[sq->head_finished_stream].head_ts, av_compare_ts(sq->streams[sq->head_finished_stream].head_ts,
sq->streams[sq->head_finished_stream].tb, sq->streams[sq->head_finished_stream].tb, ts,
ts, st->tb) <= 0) st->tb) <= 0)
finish_stream(sq, stream_idx); finish_stream(sq, stream_idx);
/* update the overall head timestamp if it could have changed */ /* update the overall head timestamp if it could have changed */
if (st->limiting && if (st->limiting && (sq->head_stream < 0 || sq->head_stream == stream_idx))
(sq->head_stream < 0 || sq->head_stream == stream_idx))
queue_head_update(sq); 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 * @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; SyncQueueStream *st;
SyncQueueFrame frame; SyncQueueFrame frame;
int64_t tail_ts = AV_NOPTS_VALUE; int64_t tail_ts = AV_NOPTS_VALUE;
@ -201,8 +305,8 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx)
st = &sq->streams[i]; st = &sq->streams[i];
if (st->head_ts != AV_NOPTS_VALUE && if (st->head_ts != AV_NOPTS_VALUE &&
(ts == AV_NOPTS_VALUE || (ts == AV_NOPTS_VALUE ||
av_compare_ts(ts, sq->streams[stream_idx].tb, av_compare_ts(ts, sq->streams[stream_idx].tb, st->head_ts,
st->head_ts, st->tb) < 0)) { st->tb) < 0)) {
ts = st->head_ts; ts = st->head_ts;
stream_idx = i; stream_idx = i;
} }
@ -215,16 +319,21 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx)
st = &sq->streams[stream_idx]; st = &sq->streams[stream_idx];
/* get the chosen stream's tail timestamp */ /* get the chosen stream's tail timestamp */
for (size_t i = 0; tail_ts == AV_NOPTS_VALUE && for (size_t i = 0;
av_fifo_peek(st->fifo, &frame, 1, i) >= 0; i++) tail_ts == AV_NOPTS_VALUE && av_fifo_peek(st->fifo, &frame, 1, i) >= 0;
tail_ts = frame_ts(sq, frame); i++)
tail_ts = frame_end(sq, frame, 0);
/* overflow triggers when the tail is over specified duration behind the head */
/* overflow triggers when the tail is over specified duration behind the
* head
*/
if (tail_ts == AV_NOPTS_VALUE || tail_ts >= st->head_ts || 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; 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++; tail_ts++;
for (unsigned int i = 0; i < sq->nb_streams; i++) { for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueStream *st1 = &sq->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) if (st1->head_ts != AV_NOPTS_VALUE)
ts = FFMAX(st1->head_ts + 1, ts); 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); stream_update_ts(sq, i, ts);
} }
return 1; 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; SyncQueueStream *st;
SyncQueueFrame dst; SyncQueueFrame dst;
int64_t ts; int64_t ts;
int ret; int ret, nb_samples;
av_assert0(stream_idx < sq->nb_streams); av_assert0(stream_idx < sq->nb_streams);
st = &sq->streams[stream_idx]; st = &sq->streams[stream_idx];
av_assert0(st->tb.num > 0 && st->tb.den > 0);
if (frame_null(sq, frame)) { if (frame_null(sq, frame)) {
av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u EOF\n", stream_idx);
finish_stream(sq, stream_idx); finish_stream(sq, stream_idx);
return 0; return 0;
} }
if (st->finished) if (st->finished)
return AVERROR_EOF; 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) if (ret < 0)
return ret; return ret;
frame_move(sq, dst, frame); 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); ret = av_fifo_write(st->fifo, &dst, 1);
if (ret < 0) { if (ret < 0) {
frame_move(sq, frame, dst); frame_move(sq, frame, dst);
objpool_release(sq->pool, (void**)&dst); objpool_release(sq->pool, (void **)&dst);
return ret; return ret;
} }
stream_update_ts(sq, stream_idx, ts); stream_update_ts(sq, stream_idx, ts);
st->frames_sent++; st->samples_queued += nb_samples;
if (st->frames_sent >= st->frames_max) 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); finish_stream(sq, stream_idx);
}
return 0; 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, static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx,
SyncQueueFrame frame) SyncQueueFrame frame) {
{ SyncQueueStream *st_head =
SyncQueueStream *st_head = sq->head_stream >= 0 ? sq->head_stream >= 0 ? &sq->streams[sq->head_stream] : NULL;
&sq->streams[sq->head_stream] : NULL;
SyncQueueStream *st; SyncQueueStream *st;
av_assert0(stream_idx < sq->nb_streams); av_assert0(stream_idx < sq->nb_streams);
st = &sq->streams[stream_idx]; 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; SyncQueueFrame peek;
int64_t ts; int64_t ts;
int cmp = 1; int cmp = 1;
if (st->finished)
nb_samples = FFMIN(nb_samples, st->samples_queued);
av_fifo_peek(st->fifo, &peek, 1, 0); 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 /* check if this stream's tail timestamp does not overtake
* the overall queue head */ * 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. /* We can release frames that do not end after the queue head.
* Frames with no timestamps are just passed through with no conditions. * 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) { if (cmp <= 0 || ts == AV_NOPTS_VALUE || !sq->have_limiting) {
frame_move(sq, frame, peek); if (nb_samples && (nb_samples != peek.f->nb_samples ||
objpool_release(sq->pool, (void**)&peek); !frame_is_aligned(sq, peek.f))) {
av_fifo_drain2(st->fifo, 1); 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 0;
} }
} }
return (sq->finished || (st->finished && !av_fifo_can_read(st->fifo))) ? return (sq->finished || (st->finished && !av_fifo_can_read(st->fifo)))
AVERROR_EOF : AVERROR(EAGAIN); ? 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 nb_eof = 0;
int ret; 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); 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); int ret = receive_internal(sq, stream_idx, frame);
/* try again if the queue overflowed and triggered a fake heartbeat /* 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; return ret;
} }
int sq_add_stream(SyncQueue *sq, int limiting) int sq_add_stream(SyncQueue *sq, int limiting) {
{
SyncQueueStream *tmp, *st; 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) if (!tmp)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
sq->streams = tmp; sq->streams = tmp;
@ -374,63 +646,64 @@ int sq_add_stream(SyncQueue *sq, int limiting)
st = &sq->streams[sq->nb_streams]; st = &sq->streams[sq->nb_streams];
memset(st, 0, sizeof(*st)); 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) if (!st->fifo)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
/* we set a valid default, so that a pathological stream that never /* 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 * receives even a real timebase (and no frames) won't stall all other
* streams forever; cf. overflow_heartbeat() */ * streams forever; cf. overflow_heartbeat() */
st->tb = (AVRational){ 1, 1 }; st->tb = (AVRational){1, 1};
st->head_ts = AV_NOPTS_VALUE; st->head_ts = AV_NOPTS_VALUE;
st->frames_max = UINT64_MAX; st->frames_max = UINT64_MAX;
st->limiting = limiting; st->limiting = limiting;
sq->have_limiting |= limiting;
return sq->nb_streams++; 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; SyncQueueStream *st;
av_assert0(stream_idx < sq->nb_streams); av_assert0(stream_idx < sq->nb_streams);
st = &sq->streams[stream_idx]; st = &sq->streams[stream_idx];
av_assert0(!av_fifo_can_read(st->fifo)); st->frames_max = frames;
if (st->frames_sent >= st->frames_max)
if (st->head_ts != AV_NOPTS_VALUE) finish_stream(sq, stream_idx);
st->head_ts = av_rescale_q(st->head_ts, st->tb, tb);
st->tb = tb;
} }
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; SyncQueueStream *st;
av_assert0(sq->type == SYNC_QUEUE_FRAMES);
av_assert0(stream_idx < sq->nb_streams); av_assert0(stream_idx < sq->nb_streams);
st = &sq->streams[stream_idx]; st = &sq->streams[stream_idx];
st->frames_max = frames; st->frame_samples = frame_samples;
if (st->frames_sent >= st->frames_max)
finish_stream(sq, stream_idx); 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)); SyncQueue *sq = av_mallocz(sizeof(*sq));
if (!sq) if (!sq)
return NULL; return NULL;
sq->type = type; sq->type = type;
sq->buf_size_us = buf_size_us; sq->buf_size_us = buf_size_us;
sq->logctx = logctx;
sq->head_stream = -1; sq->head_stream = -1;
sq->head_finished_stream = -1; sq->head_finished_stream = -1;
sq->pool = (type == SYNC_QUEUE_PACKETS) ? objpool_alloc_packets() : sq->pool = (type == SYNC_QUEUE_PACKETS) ? objpool_alloc_packets()
objpool_alloc_frames(); : objpool_alloc_frames();
if (!sq->pool) { if (!sq->pool) {
av_freep(&sq); av_freep(&sq);
return NULL; return NULL;
@ -439,8 +712,7 @@ SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us)
return sq; return sq;
} }
void sq_free(SyncQueue **psq) void sq_free(SyncQueue **psq) {
{
SyncQueue *sq = *psq; SyncQueue *sq = *psq;
if (!sq) if (!sq)
@ -449,7 +721,7 @@ void sq_free(SyncQueue **psq)
for (unsigned int i = 0; i < sq->nb_streams; i++) { for (unsigned int i = 0; i < sq->nb_streams; i++) {
SyncQueueFrame frame; SyncQueueFrame frame;
while (av_fifo_read(sq->streams[i].fifo, &frame, 1) >= 0) 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); av_fifo_freep2(&sq->streams[i].fifo);
} }

@ -1,6 +1,6 @@
/* /*
* This file is part of FFmpeg. * 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 * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * 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 * This file is the modified version of sync_queue.h file living in ffmpeg
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * 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. * by us to develop ffmpeg-kit library.
* *
* ffmpeg-kit changes by ARTHENICA LTD * ffmpeg-kit changes by ARTHENICA LTD
* *
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
@ -44,13 +49,18 @@ enum SyncQueueType {
}; };
typedef union SyncQueueFrame { typedef union SyncQueueFrame {
AVFrame *f; AVFrame *f;
AVPacket *p; AVPacket *p;
} SyncQueueFrame; } SyncQueueFrame;
#define SQFRAME(frame) ((SyncQueueFrame){ .f = (frame) }) #define SQFRAME(frame) ((SyncQueueFrame){.f = (frame)})
#define SQPKT(pkt) ((SyncQueueFrame){ .p = (pkt) }) #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; typedef struct SyncQueue SyncQueue;
/** /**
@ -58,8 +68,8 @@ typedef struct SyncQueue SyncQueue;
* *
* @param buf_size_us maximum duration that will be buffered in microseconds * @param buf_size_us maximum duration that will be buffered in microseconds
*/ */
SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us); SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us, void *logctx);
void sq_free(SyncQueue **sq); void sq_free(SyncQueue **sq);
/** /**
* Add a new stream to the sync queue. * 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); 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 * Limit the number of output frames for stream with index stream_idx
* to max_frames. * 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, void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx,
uint64_t max_frames); 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. * 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 * This file is the modified version of thread_queue.c file living in ffmpeg
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * 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. * by us to develop ffmpeg-kit library.
* *
* ffmpeg-kit changes by ARTHENICA LTD * ffmpeg-kit changes by ARTHENICA LTD
@ -49,25 +50,24 @@ enum {
}; };
typedef struct FifoElem { typedef struct FifoElem {
void *obj; void *obj;
unsigned int stream_idx; unsigned int stream_idx;
} FifoElem; } FifoElem;
struct ThreadQueue { struct ThreadQueue {
int *finished; int *finished;
unsigned int nb_streams; unsigned int nb_streams;
AVFifo *fifo; AVFifo *fifo;
ObjPool *obj_pool; ObjPool *obj_pool;
void (*obj_move)(void *dst, void *src); void (*obj_move)(void *dst, void *src);
pthread_mutex_t lock; 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; ThreadQueue *tq = *ptq;
if (!tq) if (!tq)
@ -91,8 +91,8 @@ void tq_free(ThreadQueue **ptq)
} }
ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size, 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; ThreadQueue *tq;
int ret; int ret;
@ -131,8 +131,7 @@ fail:
return NULL; 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 *finished;
int ret; int ret;
@ -153,7 +152,7 @@ int tq_send(ThreadQueue *tq, unsigned int stream_idx, void *data)
ret = AVERROR_EOF; ret = AVERROR_EOF;
*finished |= FINISHED_SEND; *finished |= FINISHED_SEND;
} else { } else {
FifoElem elem = { .stream_idx = stream_idx }; FifoElem elem = {.stream_idx = stream_idx};
ret = objpool_get(tq->obj_pool, &elem.obj); ret = objpool_get(tq->obj_pool, &elem.obj);
if (ret < 0) if (ret < 0)
@ -172,9 +171,7 @@ finish:
return ret; return ret;
} }
static int receive_locked(ThreadQueue *tq, int *stream_idx, static int receive_locked(ThreadQueue *tq, int *stream_idx, void *data) {
void *data)
{
FifoElem elem; FifoElem elem;
unsigned int nb_finished = 0; 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 */ /* return EOF to the consumer at most once for each stream */
if (!(tq->finished[i] & FINISHED_RECV)) { if (!(tq->finished[i] & FINISHED_RECV)) {
tq->finished[i] |= FINISHED_RECV; tq->finished[i] |= FINISHED_RECV;
*stream_idx = i; *stream_idx = i;
return AVERROR_EOF; 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); 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; int ret;
*stream_idx = -1; *stream_idx = -1;
@ -228,8 +224,7 @@ int tq_receive(ThreadQueue *tq, int *stream_idx, void *data)
return ret; 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); av_assert0(stream_idx < tq->nb_streams);
pthread_mutex_lock(&tq->lock); pthread_mutex_lock(&tq->lock);
@ -243,8 +238,7 @@ void tq_send_finish(ThreadQueue *tq, unsigned int stream_idx)
pthread_mutex_unlock(&tq->lock); 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); av_assert0(stream_idx < tq->nb_streams);
pthread_mutex_lock(&tq->lock); 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 * This file is the modified version of thread_queue.h file living in ffmpeg
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * 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. * by us to develop ffmpeg-kit library.
* *
* ffmpeg-kit changes by ARTHENICA LTD * ffmpeg-kit changes by ARTHENICA LTD
@ -50,8 +51,9 @@ typedef struct ThreadQueue ThreadQueue;
* @param callback that moves the contents between two data pointers * @param callback that moves the contents between two data pointers
*/ */
ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size, 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 tq_free(ThreadQueue **tq); void (*obj_move)(void *dst, void *src));
void tq_free(ThreadQueue **tq);
/** /**
* Send an item for the given stream to the queue. * Send an item for the given stream to the queue.

@ -101,4 +101,11 @@ public class AbiDetect {
*/ */
native static String getNativeBuildConf(); 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) 2018-2022 Taner Sener
* Copyright (c) 2024 ARTHENICA LTD
* *
* This file is part of FFmpegKit. * This file is part of FFmpegKit.
* *
@ -34,6 +35,8 @@ import com.arthenica.smartexception.java.Exceptions;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -46,6 +49,7 @@ import java.util.StringTokenizer;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -113,7 +117,7 @@ public class FFmpegKitConfig {
/* Session history variables */ /* Session history variables */
private static int sessionHistorySize; private static int sessionHistorySize;
private static final Map<Long, Session> sessionHistoryMap; 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 final Object sessionHistoryLock;
private static int asyncConcurrencyLimit; private static int asyncConcurrencyLimit;
@ -128,6 +132,7 @@ public class FFmpegKitConfig {
private static final SparseArray<SAFProtocolUrl> safIdMap; private static final SparseArray<SAFProtocolUrl> safIdMap;
private static final SparseArray<SAFProtocolUrl> safFileDescriptorMap; private static final SparseArray<SAFProtocolUrl> safFileDescriptorMap;
private static LogRedirectionStrategy globalLogRedirectionStrategy; private static LogRedirectionStrategy globalLogRedirectionStrategy;
private static final AtomicBoolean safUrlsReusable;
static { static {
@ -160,7 +165,7 @@ public class FFmpegKitConfig {
return (this.size() > sessionHistorySize); return (this.size() > sessionHistorySize);
} }
}; };
sessionHistoryList = new LinkedList<>(); sessionHistoryList = new ArrayList<>();
sessionHistoryLock = new Object(); sessionHistoryLock = new Object();
globalLogCallback = null; globalLogCallback = null;
@ -172,8 +177,9 @@ public class FFmpegKitConfig {
safIdMap = new SparseArray<>(); safIdMap = new SparseArray<>();
safFileDescriptorMap = new SparseArray<>(); safFileDescriptorMap = new SparseArray<>();
globalLogRedirectionStrategy = LogRedirectionStrategy.PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED; 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>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 * @param context application context
* @return the full path of the named pipe * @return the full path of the named pipe
@ -567,18 +574,17 @@ public class FFmpegKitConfig {
* @return FFmpegKit version * @return FFmpegKit version
*/ */
public static String getVersion() { public static String getVersion() {
if (isLTSBuild()) { return getNativeVersion();
return String.format("%s-lts", getNativeVersion());
} else {
return getNativeVersion();
}
} }
/** /**
* <p>Returns whether FFmpegKit release is a Long Term Release or not. * <p>Returns whether FFmpegKit release is a Long Term Release or not.
* *
* @return true/yes or false/no * @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() { public static boolean isLTSBuild() {
return AbiDetect.isNativeLTSBuild(); return AbiDetect.isNativeLTSBuild();
} }
@ -1029,6 +1035,7 @@ public class FFmpegKitConfig {
safUrl.setParcelFileDescriptor(parcelFileDescriptor); safUrl.setParcelFileDescriptor(parcelFileDescriptor);
final int fd = parcelFileDescriptor.getFd(); final int fd = parcelFileDescriptor.getFd();
safFileDescriptorMap.put(fd, safUrl); safFileDescriptorMap.put(fd, safUrl);
android.util.Log.d(TAG, String.format("Generated fd %d for SAF id %d.", fd, safId));
return fd; return fd;
} else { } else {
android.util.Log.e(TAG, String.format("SAF id %d not found.", safId)); android.util.Log.e(TAG, String.format("SAF id %d not found.", safId));
@ -1050,11 +1057,15 @@ public class FFmpegKitConfig {
try { try {
final SAFProtocolUrl safProtocolUrl = safFileDescriptorMap.get(fileDescriptor); final SAFProtocolUrl safProtocolUrl = safFileDescriptorMap.get(fileDescriptor);
if (safProtocolUrl != null) { if (safProtocolUrl != null) {
final int safId = safProtocolUrl.getSafId();
ParcelFileDescriptor parcelFileDescriptor = safProtocolUrl.getParcelFileDescriptor(); ParcelFileDescriptor parcelFileDescriptor = safProtocolUrl.getParcelFileDescriptor();
if (parcelFileDescriptor != null) { if (parcelFileDescriptor != null) {
safFileDescriptorMap.delete(fileDescriptor); safFileDescriptorMap.delete(fileDescriptor);
safIdMap.delete(safProtocolUrl.getSafId());
parcelFileDescriptor.close(); 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; return 1;
} else { } else {
android.util.Log.e(TAG, String.format("ParcelFileDescriptor for SAF fd %d not found.", fileDescriptor)); android.util.Log.e(TAG, String.format("ParcelFileDescriptor for SAF fd %d not found.", fileDescriptor));
@ -1069,6 +1080,31 @@ public class FFmpegKitConfig {
return 0; 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. * 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. * Returns the last session created from the session history.
* *
@ -1295,6 +1345,31 @@ public class FFmpegKitConfig {
FFmpegKitConfig.globalLogRedirectionStrategy = logRedirectionStrategy; 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. * Converts session state to string.
* *
@ -1423,6 +1498,13 @@ public class FFmpegKitConfig {
*/ */
private native static String getNativeVersion(); 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. * <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() { static String loadPackageName() {
if (isTestModeDisabled()) { if (isTestModeDisabled()) {
return Packages.getPackageName(); return Packages.getPackageName();
@ -85,25 +93,15 @@ public class NativeLoader {
} }
static String loadVersion() { static String loadVersion() {
final String version = "6.0"; final String version = "6.1.2";
if (isTestModeDisabled()) { if (isTestModeDisabled()) {
return FFmpegKitConfig.getVersion(); return FFmpegKitConfig.getVersion();
} else if (loadIsLTSBuild()) {
return String.format("%s-lts", version);
} else { } else {
return version; return version;
} }
} }
static boolean loadIsLTSBuild() {
if (isTestModeDisabled()) {
return AbiDetect.isNativeLTSBuild();
} else {
return true;
}
}
static int loadLogLevel() { static int loadLogLevel() {
if (isTestModeDisabled()) { if (isTestModeDisabled()) {
return FFmpegKitConfig.getNativeLogLevel(); return FFmpegKitConfig.getNativeLogLevel();

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

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2018-2021 Taner Sener * Copyright (c) 2018-2021 Taner Sener
* Copyright (c) 2024 ARTHENICA LTD
* *
* This file is part of FFmpegKit. * 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) { private String listToPackageName(final List<String> externalLibraryList) {
boolean speex = externalLibraryList.contains("speex"); boolean speex = externalLibraryList.contains("speex");
boolean fribidi = externalLibraryList.contains("fribidi"); boolean fribidi = externalLibraryList.contains("fribidi");

Binary file not shown.

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with 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 # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" # This is normally unused
APP_BASE_NAME=`basename "$0"` # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 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 [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 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." location of your Java installation."
fi fi
else else
JAVACMD="java" 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. 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 Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
MAX_FD="$MAX_FD_LIMIT" # shellcheck disable=SC3045
fi MAX_FD=$( ulimit -H -n ) ||
ulimit -n $MAX_FD warn "Could not query maximum file descriptor limit"
if [ $? -ne 0 ] ; then esac
warn "Could not set maximum file descriptor limit: $MAX_FD" case $MAX_FD in #(
fi '' | soft) :;; #(
else *)
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
fi # shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi fi
# For Darwin, add options to specify how the application appears in the dock # Collect all arguments for the java command, stacking in reverse order:
if $darwin; then # * args from the command line
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" # * the main class name
fi # * -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 # For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then if "$cygwin" || "$msys" ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=$( cygpath --unix "$JAVACMD" )
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh # Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0 for arg do
for arg in "$@" ; do if
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` case $arg in #(
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option -*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition [ -e "$t" ] ;; #(
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` *) false ;;
else esac
eval `echo args$i`="\"$arg\"" then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi 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 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 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 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 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" "$@" exec "$JAVACMD" "$@"

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

@ -1,59 +1,50 @@
MY_LOCAL_PATH := $(call my-dir) MY_LOCAL_PATH := $(call my-dir)
$(call import-add-path, $(MY_LOCAL_PATH)) $(call import-add-path, $(MY_LOCAL_PATH))
include $(MY_LOCAL_PATH)/build.mk
MY_ARMV7 := false MY_ARMV7 := false
MY_ARMV7_NEON := false MY_ARMV7_NEON := false
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a) ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
ifeq ("$(shell test -e $(MY_LOCAL_PATH)/../build/.armv7 && echo armv7)","armv7") MY_ARMV7 := ${ARMV7}
MY_ARMV7 := true MY_ARMV7_NEON := ${ARMV7_NEON}
endif
ifeq ("$(shell test -e $(MY_LOCAL_PATH)/../build/.armv7neon && echo armv7neon)","armv7neon")
MY_ARMV7_NEON := true
endif
endif endif
ifeq ("$(shell test -e $(MY_LOCAL_PATH)/../build/.lts && echo lts)","lts") # DEFINE ARCH FLAGS
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
MY_ARM_MODE := arm MY_ARM_MODE := arm
MY_ARM_NEON := false 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) ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
MY_ARCH_FLAGS := ARM_V7A MY_ARCH_FLAGS := ARM_V7A
ifeq ("$(shell test -e $(MY_LOCAL_PATH)/../build/.lts && echo lts)","lts") MY_ARM_NEON := true
MY_ARM_NEON := false ifeq ($(MY_ARMV7_NEON), true)
MY_BUILD_DIR := $(ARMV7_NEON_BUILD_PATH)
else else
MY_ARM_NEON := true MY_BUILD_DIR := $(ARMV7_BUILD_PATH)
endif endif
endif endif
ifeq ($(TARGET_ARCH_ABI), arm64-v8a) ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
MY_ARCH_FLAGS := ARM64_V8A MY_ARCH_FLAGS := ARM64_V8A
MY_ARM_NEON := true MY_ARM_NEON := true
MY_BUILD_DIR := $(ARM64_BUILD_PATH)
endif endif
ifeq ($(TARGET_ARCH_ABI), x86) ifeq ($(TARGET_ARCH_ABI), x86)
MY_ARCH_FLAGS := X86 MY_ARCH_FLAGS := X86
MY_ARM_NEON := true MY_ARM_NEON := true
MY_BUILD_DIR := $(X86_BUILD_PATH)
endif endif
ifeq ($(TARGET_ARCH_ABI), x86_64) ifeq ($(TARGET_ARCH_ABI), x86_64)
MY_ARCH_FLAGS := X86_64 MY_ARCH_FLAGS := X86_64
MY_ARM_NEON := true 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 endif
include $(CLEAR_VARS) include $(CLEAR_VARS)
@ -62,6 +53,7 @@ LOCAL_MODULE := ffmpegkit_abidetect
LOCAL_SRC_FILES := ffmpegkit_abidetect.c LOCAL_SRC_FILES := ffmpegkit_abidetect.c
LOCAL_CFLAGS := -Wall -Wextra -Werror -Wno-unused-parameter -DFFMPEG_KIT_${MY_ARCH_FLAGS} LOCAL_CFLAGS := -Wall -Wextra -Werror -Wno-unused-parameter -DFFMPEG_KIT_${MY_ARCH_FLAGS}
LOCAL_C_INCLUDES := $(FFMPEG_INCLUDES) LOCAL_C_INCLUDES := $(FFMPEG_INCLUDES)
LOCAL_LDFLAGS := $(MY_LDFLAGS)
LOCAL_LDLIBS := -llog -lz -landroid LOCAL_LDLIBS := -llog -lz -landroid
LOCAL_STATIC_LIBRARIES := cpu-features LOCAL_STATIC_LIBRARIES := cpu-features
LOCAL_ARM_NEON := ${MY_ARM_NEON} LOCAL_ARM_NEON := ${MY_ARM_NEON}
@ -69,13 +61,7 @@ include $(BUILD_SHARED_LIBRARY)
$(call import-module, cpu-features) $(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 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
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_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare MY_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare
MY_LDLIBS := -llog -lz -landroid MY_LDLIBS := -llog -lz -landroid
@ -89,6 +75,7 @@ ifeq ($(MY_ARMV7_NEON), true)
LOCAL_MODULE := ffmpegkit_armv7a_neon LOCAL_MODULE := ffmpegkit_armv7a_neon
LOCAL_SRC_FILES := $(MY_SRC_FILES) LOCAL_SRC_FILES := $(MY_SRC_FILES)
LOCAL_CFLAGS := $(MY_CFLAGS) LOCAL_CFLAGS := $(MY_CFLAGS)
LOCAL_LDFLAGS := $(MY_LDFLAGS)
LOCAL_LDLIBS := $(MY_LDLIBS) LOCAL_LDLIBS := $(MY_LDLIBS)
LOCAL_SHARED_LIBRARIES := libavcodec_neon libavfilter_neon libswscale_neon libavformat_neon libavutil_neon libswresample_neon libavdevice_neon LOCAL_SHARED_LIBRARIES := libavcodec_neon libavfilter_neon libswscale_neon libavformat_neon libavutil_neon libswresample_neon libavdevice_neon
ifeq ($(APP_STL), c++_shared) ifeq ($(APP_STL), c++_shared)
@ -111,6 +98,7 @@ ifeq ($(MY_BUILD_GENERIC_FFMPEG_KIT), true)
LOCAL_MODULE := ffmpegkit LOCAL_MODULE := ffmpegkit
LOCAL_SRC_FILES := $(MY_SRC_FILES) LOCAL_SRC_FILES := $(MY_SRC_FILES)
LOCAL_CFLAGS := $(MY_CFLAGS) LOCAL_CFLAGS := $(MY_CFLAGS)
LOCAL_LDFLAGS := $(MY_LDFLAGS)
LOCAL_LDLIBS := $(MY_LDLIBS) LOCAL_LDLIBS := $(MY_LDLIBS)
LOCAL_SHARED_LIBRARIES := libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale LOCAL_SHARED_LIBRARIES := libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
ifeq ($(APP_STL), c++_shared) ifeq ($(APP_STL), c++_shared)

@ -1,8 +1,16 @@
ifeq ($(MY_LTS_POSTFIX),-lts) ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
LOCAL_PATH := $(call my-dir)/../../../prebuilt/android-$(TARGET_ARCH)-lts/ffmpeg/lib MY_BUILD_DIR := $(ARMV7_BUILD_PATH)
else
LOCAL_PATH := $(call my-dir)/../../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/lib
endif 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 MY_ARM_MODE := arm

@ -1,8 +1,4 @@
ifeq ($(MY_LTS_POSTFIX),-lts) LOCAL_PATH := $(call my-dir)/../../../../prebuilt/$(MY_BUILD_DIR)/ffmpeg/lib
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
MY_ARM_MODE := arm MY_ARM_MODE := arm
MY_ARM_NEON := true 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 "Options:"
echo -e " -h, --help\t\t\tdisplay this help and exit" echo -e " -h, --help\t\t\tdisplay this help and exit"
echo -e " -v, --version\t\t\tdisplay version information and exit" echo -e " -v, --version\t\t\tdisplay version information and exit"
echo -e " -f, --force\t\t\tignore warnings" echo -e " -f, --force\t\t\tignore warnings\n"
echo -e " -l, --lts\t\t\tinclude lts packages to support iOS 10+, tvOS 10+, macOS 10.12+ devices\n"
echo -e "Architectures:" echo -e "Architectures:"
echo -e " --disable-iphoneos\t\tdo not include iphoneos architecture variant [yes]" 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" export FFMPEG_KIT_BUILD_TYPE="apple"
source "${BASEDIR}"/scripts/function-${FFMPEG_KIT_BUILD_TYPE}.sh source "${BASEDIR}"/scripts/function-${FFMPEG_KIT_BUILD_TYPE}.sh
# SET DEFAULTS SETTINGS # SET DEFAULT SETTINGS
enable_default_architecture_variants enable_default_architecture_variants
# SELECT XCODE VERSION USED FOR BUILDING # 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_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_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)" 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 echo -e "INFO: Build options: $*\n" 1>>"${BASEDIR}"/build.log 2>&1
# SET DEFAULT BUILD OPTIONS # SET DEFAULT BUILD OPTIONS
@ -152,14 +152,6 @@ if [[ -z ${BUILD_VERSION} ]]; then
exit 1 exit 1
fi 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 # PROCESS BUILD OPTIONS
while [ ! $# -eq 0 ]; do while [ ! $# -eq 0 ]; do
case $1 in case $1 in
@ -170,7 +162,6 @@ while [ ! $# -eq 0 ]; do
display_version display_version
exit 0 exit 0
;; ;;
-l | --lts) ;;
-f | --force) -f | --force)
export BUILD_FORCE="1" export BUILD_FORCE="1"
;; ;;

10
apple/.gitignore vendored

@ -1,5 +1,6 @@
/autom4te.cache/ /autom4te.cache/
/Makefile /Makefile
/Makefile.in
/config.status /config.status
/configure.tmp /configure.tmp
/configure~ /configure~
@ -14,4 +15,11 @@
/configure /configure
/libtool /libtool
/ltmain.sh /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 # could be handy for archiving the generated documentation or if some version
# control system is used. # 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 # 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 # 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_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([src/FFmpegKit.m]) 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
/Makefile.in
/.deps/ /.deps/
*.o *.o
*.lo *.lo

@ -20,8 +20,8 @@
#ifndef FFMPEG_KIT_ABSTRACT_SESSION_H #ifndef FFMPEG_KIT_ABSTRACT_SESSION_H
#define FFMPEG_KIT_ABSTRACT_SESSION_H #define FFMPEG_KIT_ABSTRACT_SESSION_H
#import <Foundation/Foundation.h>
#import "Session.h" #import "Session.h"
#import <Foundation/Foundation.h>
/** /**
* Defines how long default "getAll" methods wait, in milliseconds. * Defines how long default "getAll" methods wait, in milliseconds.
@ -29,10 +29,11 @@
extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
/** /**
* Abstract session implementation which includes common features shared by <code>FFmpeg</code>, * Abstract session implementation which includes common features shared by
* <code>FFprobe</code> and <code>MediaInformation</code> sessions. * <code>FFmpeg</code>, <code>FFprobe</code> and <code>MediaInformation</code>
* sessions.
*/ */
@interface AbstractSession : NSObject<Session> @interface AbstractSession : NSObject <Session>
/** /**
* Creates a new abstract session. * Creates a new abstract session.
@ -41,10 +42,13 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
* @param logCallback session specific log callback * @param logCallback session specific log callback
* @param logRedirectionStrategy session specific log redirection strategy * @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 * @param timeout wait timeout in milliseconds
*/ */

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

@ -32,14 +32,21 @@
* *
* @return architecture name of the cpu running * @return architecture name of the cpu running
*/ */
+ (NSString*)getCpuArch; + (NSString *)getCpuArch;
/** /**
* Returns architecture name loaded. * Returns architecture name loaded.
* *
* @return 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 @end

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

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

@ -22,62 +22,63 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
extern NSString* const ChapterKeyId; extern NSString *const ChapterKeyId;
extern NSString* const ChapterKeyTimeBase; extern NSString *const ChapterKeyTimeBase;
extern NSString* const ChapterKeyStart; extern NSString *const ChapterKeyStart;
extern NSString* const ChapterKeyStartTime; extern NSString *const ChapterKeyStartTime;
extern NSString* const ChapterKeyEnd; extern NSString *const ChapterKeyEnd;
extern NSString* const ChapterKeyEndTime; extern NSString *const ChapterKeyEndTime;
extern NSString* const ChapterKeyTags; extern NSString *const ChapterKeyTags;
/** /**
* Chapter class. * Chapter class.
*/ */
@interface Chapter : NSObject @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. * Returns the chapter property associated with the key.
* *
* @return chapter property as string or nil if the key is not found * @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. * Returns the chapter property associated with the key.
* *
* @return chapter property as number or nil if the key is not found * @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. * Returns the chapter property associated with the key.
* *
* @return chapter property as id or nil if the key is not found * @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. * Returns all chapter properties defined.
* *
* @return all chapter properties in a dictionary or nil if no properties are defined * @return all chapter properties in a dictionary or nil if no properties are
*/ * defined
- (NSDictionary*)getAllProperties; */
- (NSDictionary *)getAllProperties;
@end @end

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

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

@ -31,65 +31,123 @@
[FFmpegKitConfig class]; [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 *)executeWithArguments:(NSArray *)arguments {
FFmpegSession* session = [FFmpegSession create:arguments]; FFmpegSession *session = [FFmpegSession create:arguments];
[FFmpegKitConfig ffmpegExecute:session]; [FFmpegKitConfig ffmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback { + (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback]; withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session]; [FFmpegKitConfig asyncFFmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback { + (FFmpegSession *)
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback]; 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]; [FFmpegKitConfig asyncFFmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback]; withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFmpegSession *)
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback]; 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]; [FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFmpegSession*)execute:(NSString*)command { + (FFmpegSession *)execute:(NSString *)command {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command]]; FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig ffmpegExecute:session]; [FFmpegKitConfig ffmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback { + (FFmpegSession *)executeAsync:(NSString *)command
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback]; withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session]; [FFmpegKitConfig asyncFFmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback { + (FFmpegSession *)executeAsync:(NSString *)command
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback]; 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]; [FFmpegKitConfig asyncFFmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFmpegSession *)executeAsync:(NSString *)command
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback]; withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFmpegSession *)executeAsync:(NSString *)command
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback]; 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]; [FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session; return session;
} }
@ -98,7 +156,8 @@
/* /*
* ZERO (0) IS A SPECIAL SESSION ID * 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); cancel_operation(0);
} }
@ -107,7 +166,7 @@
cancel_operation(sessionId); cancel_operation(sessionId);
} }
+ (NSArray*)listSessions { + (NSArray *)listSessions {
return [FFmpegKitConfig getFFmpegSessions]; return [FFmpegKitConfig getFFmpegSessions];
} }

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

File diff suppressed because it is too large Load Diff

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

@ -25,33 +25,63 @@
@implementation FFmpegSession { @implementation FFmpegSession {
StatisticsCallback _statisticsCallback; StatisticsCallback _statisticsCallback;
FFmpegSessionCompleteCallback _completeCallback; FFmpegSessionCompleteCallback _completeCallback;
NSMutableArray* _statistics; NSMutableArray *_statistics;
NSRecursiveLock* _statisticsLock; NSRecursiveLock *_statisticsLock;
} }
+ (void)initialize { + (void)initialize {
// EMPTY INITIALIZE // EMPTY INITIALIZE
} }
+ (instancetype)create:(NSArray*)arguments { + (instancetype)create:(NSArray *)arguments {
return [[self alloc] init:arguments withCompleteCallback:nil withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]]; return [[self alloc] init:arguments
} withCompleteCallback:nil
withLogCallback:nil
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback { withStatisticsCallback:nil
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]]; withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
} }
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback { + (instancetype)create:(NSArray *)arguments
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]]; withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
} return [[self alloc] init:arguments
withCompleteCallback:completeCallback
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy { withLogCallback:nil
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback withLogRedirectionStrategy:logRedirectionStrategy]; withStatisticsCallback:nil
} withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
+ (instancetype)create:(NSArray *)arguments
self = [super init:arguments withLogCallback:logCallback withLogRedirectionStrategy:logRedirectionStrategy]; 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) { if (self) {
_statisticsCallback = statisticsCallback; _statisticsCallback = statisticsCallback;
@ -71,30 +101,36 @@
return _completeCallback; return _completeCallback;
} }
- (NSArray*)getAllStatisticsWithTimeout:(int)waitTimeout { - (NSArray *)getAllStatisticsWithTimeout:(int)waitTimeout {
[self waitForAsynchronousMessagesInTransmit:waitTimeout]; [self waitForAsynchronousMessagesInTransmit:waitTimeout];
if ([self thereAreAsynchronousMessagesInTransmit]) { 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]; return [self getStatistics];
} }
- (NSArray*)getAllStatistics { - (NSArray *)getAllStatistics {
return [self getAllStatisticsWithTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit]; return [self
getAllStatisticsWithTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
} }
- (NSArray*)getStatistics { - (NSArray *)getStatistics {
[_statisticsLock lock]; [_statisticsLock lock];
NSArray* statisticsCopy = [_statistics copy]; NSArray *statisticsCopy = [_statistics copy];
[_statisticsLock unlock]; [_statisticsLock unlock];
return statisticsCopy; return statisticsCopy;
} }
- (Statistics*)getLastReceivedStatistics { - (Statistics *)getLastReceivedStatistics {
Statistics* lastStatistics = nil; Statistics *lastStatistics = nil;
[_statisticsLock lock]; [_statisticsLock lock];
if ([_statistics count] > 0) { if ([_statistics count] > 0) {
@ -105,7 +141,7 @@
return lastStatistics; return lastStatistics;
} }
- (void)addStatistics:(Statistics*)statistics { - (void)addStatistics:(Statistics *)statistics {
[_statisticsLock lock]; [_statisticsLock lock];
[_statistics addObject:statistics]; [_statistics addObject:statistics];
[_statisticsLock unlock]; [_statisticsLock unlock];
@ -124,4 +160,3 @@
} }
@end @end

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

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

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

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

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

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

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2021 Taner Sener * Copyright (c) 2021 Taner Sener
* Copyright (c) 2024 ARTHENICA LTD
* *
* This file is part of FFmpegKit. * This file is part of FFmpegKit.
* *
@ -23,11 +24,12 @@
/** /**
* <p>Enumeration type for log levels. * <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 * This log level is defined by FFmpegKit. It is used to specify logs
* FFmpeg. Logs that has this level are not filtered and always redirected. * printed to stderr by FFmpeg. Logs that has this level are not filtered
* and always redirected.
*/ */
LevelAVLogStdErr = -16, LevelAVLogStdErr = -16,

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

Loading…
Cancel
Save