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. 14
      .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. 14
      README.md
  11. 93
      android.sh
  12. 2
      android/build.gradle
  13. 2
      android/ffmpeg-kit-android-lib/Doxyfile
  14. 13
      android/ffmpeg-kit-android-lib/build.gradle
  15. 3
      android/ffmpeg-kit-android-lib/src/main/cpp/.gitignore
  16. 20
      android/ffmpeg-kit-android-lib/src/main/cpp/android_support.c
  17. 182
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpeg_context.c
  18. 144
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpeg_context.h
  19. 355
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.c
  20. 73
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.h
  21. 60
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_abidetect.c
  22. 23
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_abidetect.h
  23. 2
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_exception.h
  24. 17
      android/ffmpeg-kit-android-lib/src/main/cpp/ffprobekit.c
  25. 5
      android/ffmpeg-kit-android-lib/src/main/cpp/ffprobekit.h
  26. 514
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.c
  27. 128
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.h
  28. 5250
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.c
  29. 475
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.h
  30. 1168
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_dec.c
  31. 1347
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_demux.c
  32. 962
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_enc.c
  33. 3026
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_filter.c
  34. 320
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_hw.c
  35. 500
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.c
  36. 63
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.h
  37. 2372
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux_init.c
  38. 801
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_opt.c
  39. 2852
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffprobe.c
  40. 14
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_fopen_utf8.h
  41. 51
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.c
  42. 7
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.h
  43. 544
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.c
  44. 8
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.h
  45. 438
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.c
  46. 34
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.h
  47. 28
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.c
  48. 8
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.h
  49. 7
      android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/AbiDetect.java
  50. 100
      android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java
  51. 20
      android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/NativeLoader.java
  52. 4
      android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/Packages.java
  53. 6
      android/ffmpeg-kit-android-lib/src/test/java/com/arthenica/ffmpegkit/FFmpegKitConfigTest.java
  54. BIN
      android/gradle/wrapper/gradle-wrapper.jar
  55. 4
      android/gradle/wrapper/gradle-wrapper.properties
  56. 281
      android/gradlew
  57. 11
      android/gradlew.bat
  58. 62
      android/jni/Android.mk
  59. 16
      android/jni/ffmpeg/Android.mk
  60. 6
      android/jni/ffmpeg/neon/Android.mk
  61. 17
      apple.sh
  62. 10
      apple/.gitignore
  63. 2
      apple/Doxyfile
  64. 1251
      apple/aclocal.m4
  65. 271
      apple/ar-lib
  66. 348
      apple/compile
  67. 1748
      apple/config.guess
  68. 1884
      apple/config.sub
  69. 4
      apple/configure.ac
  70. 791
      apple/depcomp
  71. 541
      apple/install-sh
  72. 215
      apple/missing
  73. 1
      apple/src/.gitignore
  74. 14
      apple/src/AbstractSession.h
  75. 39
      apple/src/AbstractSession.m
  76. 7
      apple/src/ArchDetect.h
  77. 23
      apple/src/ArchDetect.m
  78. 2
      apple/src/AtomicLong.m
  79. 3
      apple/src/Chapter.h
  80. 1
      apple/src/Chapter.m
  81. 174
      apple/src/FFmpegKit.h
  82. 97
      apple/src/FFmpegKit.m
  83. 251
      apple/src/FFmpegKitConfig.h
  84. 549
      apple/src/FFmpegKitConfig.m
  85. 44
      apple/src/FFmpegSession.h
  86. 77
      apple/src/FFmpegSession.m
  87. 13
      apple/src/FFmpegSessionCompleteCallback.h
  88. 335
      apple/src/FFprobeKit.h
  89. 222
      apple/src/FFprobeKit.m
  90. 14
      apple/src/FFprobeSession.h
  91. 42
      apple/src/FFprobeSession.m
  92. 19
      apple/src/FFprobeSessionCompleteCallback.h
  93. 8
      apple/src/Level.h
  94. 2
      apple/src/LogCallback.h
  95. 4
      apple/src/Makefile.am
  96. 1164
      apple/src/Makefile.in
  97. 9
      apple/src/MediaInformation.h
  98. 5
      apple/src/MediaInformation.m
  99. 6
      apple/src/MediaInformationJsonParser.h
  100. 31
      apple/src/MediaInformationJsonParser.m
  101. Some files were not shown because too many files have changed in this diff Show More

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

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

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

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

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

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

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

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

1
.gitignore vendored

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

@ -1,19 +1,5 @@
# FFmpegKit ![GitHub release](https://img.shields.io/badge/release-v6.0-blue.svg) ![Maven Central](https://img.shields.io/maven-central/v/com.arthenica/ffmpeg-kit-min) ![CocoaPods](https://img.shields.io/cocoapods/v/ffmpeg-kit-ios-min) ![pub](https://img.shields.io/pub/v/ffmpeg_kit_flutter.svg) ![npm](https://img.shields.io/npm/v/ffmpeg-kit-react-native.svg)
## Notice
FFmpegKit has been officially retired. There will be no further `ffmpeg-kit` releases.
See [Saying Goodbye to FFmpegKit @ medium](https://medium.com/@tanersener/saying-goodbye-to-ffmpegkit-33ae939767e1) to learn why we made this decision.
All previously released `ffmpeg-kit` binaries will be removed according to the following schedule.
| FFmpegKit Version | Available Until |
|:-----------------:|:------------------:|
| Less than 6.0 | February 1st, 2025 |
| 6.0 | April 1st, 2025 |
Thank you for your support and interest in this project.
<img src="https://github.com/arthenica/ffmpeg-kit/blob/main/docs/assets/ffmpeg-kit-icon-v9.png" width="240">
`FFmpegKit` is a collection of tools to use `FFmpeg`<sup>1</sup> in `Android`, `iOS`, `Linux`, `macOS`, `tvOS`, `Flutter` and `React Native` applications.

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

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

@ -38,7 +38,7 @@ PROJECT_NAME = "FFmpegKit Android API"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 6.0
PROJECT_NUMBER = 6.1.2
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

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

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

@ -26,15 +26,18 @@
* SUCH DAMAGE.
*/
#include <math.h>
#include <errno.h>
#include <malloc.h>
#include <math.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
// posix_memalign() and memalign() are not available in the NDK
// __INTRODUCED_IN(17)
#if __ANDROID_API__ < 17
int posix_memalign(void **memptr, size_t alignment, size_t size) {
@ -56,13 +59,16 @@ int posix_memalign(void** memptr, size_t alignment, size_t size) {
#endif /* __ANDROID_API__ < 17 */
double log2(double x) {
return (log(x) / M_LN2);
}
// log2() and log2f() are C99 functions, but they're not available in the NDK
// __INTRODUCED_IN(18)
float log2f(float x) {
return (float) log2((double) x);
}
#if __ANDROID_API__ < 18
double log2(double x) { return (log(x) / M_LN2); }
float log2f(float x) { return (float)log2((double)x); }
#endif /* __ANDROID_API__ < 18 */
#ifdef __cplusplus
}; /* end of extern "C" */

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

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

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

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

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

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

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

@ -18,14 +18,14 @@
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "config.h"
#include "ffmpegkit.h"
#include "libavcodec/jni.h"
#include "libavutil/bprint.h"
#include "libavutil/mem.h"
#include "ffmpegkit.h"
/** Forward declaration for function defined in fftools_ffprobe.c */
int ffprobe_execute(int argc, char **argv);
@ -45,7 +45,9 @@ extern void resetMessagesInTransmit(long sessionId);
* @param stringArray reference to the object holding FFprobe command arguments
* @return zero on successful execution, non-zero on error
*/
JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute(JNIEnv *env, jclass object, jlong id, jobjectArray stringArray) {
JNIEXPORT jint JNICALL
Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute(
JNIEnv *env, jclass object, jlong id, jobjectArray stringArray) {
jstring *tempArray = NULL;
int argumentCount = 1;
char **argv = NULL;
@ -57,7 +59,8 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprob
int programArgumentCount = (*env)->GetArrayLength(env, stringArray);
argumentCount = programArgumentCount + 1;
tempArray = (jstring *) av_malloc(sizeof(jstring) * programArgumentCount);
tempArray =
(jstring *)av_malloc(sizeof(jstring) * programArgumentCount);
}
/* PRESERVE USAGE FORMAT
@ -71,9 +74,11 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprob
// PREPARE ARRAY ELEMENTS
if (stringArray) {
for (int i = 0; i < (argumentCount - 1); i++) {
tempArray[i] = (jstring) (*env)->GetObjectArrayElement(env, stringArray, i);
tempArray[i] =
(jstring)(*env)->GetObjectArrayElement(env, stringArray, i);
if (tempArray[i] != NULL) {
argv[i + 1] = (char *) (*env)->GetStringUTFChars(env, tempArray[i], 0);
argv[i + 1] =
(char *)(*env)->GetStringUTFChars(env, tempArray[i], 0);
}
}
}

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

@ -2,7 +2,7 @@
* Various utilities for command line tools
* Copyright (c) 2000-2003 Fabrice Bellard
* Copyright (c) 2018-2022 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
* Copyright (c) 2023-2024 ARTHENICA LTD
*
* This file is part of FFmpeg.
*
@ -22,12 +22,18 @@
*/
/*
* This file is the modified version of cmdutils.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* This file is the modified version of cmdutils.c file living in ffmpeg source
* code under the fftools folder. We manually update it each time we depend on a
* new ffmpeg version. Below you can see the list of changes applied by us to
* develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
* - longjmp_value dropped
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
@ -36,18 +42,21 @@
*
* 09.2022
* --------------------------------------------------------
* - Dropped the prototypes of init_report, ffmpegkit_log_callback_function and report_callback functions
* - Dropped the prototypes of init_report, ffmpegkit_log_callback_function and
* report_callback functions
* - volatile dropped from thread local variables
*
* 01.2020
* --------------------------------------------------------
* - ffprobe support added (variables used by ffprobe marked with "__thread" specifier, logs with AV_LOG_INFO level
* migrated to use AV_LOG_STDERR)
* - (optindex < argc) validation in parse_options() method updated with (optindex >= argc) check
* - ffprobe support added (variables used by ffprobe marked with "__thread"
* specifier, logs with AV_LOG_INFO level migrated to use AV_LOG_STDERR)
* - (optindex < argc) validation in parse_options() method updated with
* (optindex >= argc) check
*
* 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)
* - log_callback_report method re-added to fix -report option issues
*
* 08.2018
@ -57,49 +66,51 @@
* 07.2018
* --------------------------------------------------------
* - unused headers removed
* - parentheses placed around assignments in conditions to prevent -Wparentheses warning
* - parentheses placed around assignments in conditions to prevent
* -Wparentheses warning
* - exit_program updated with longjmp, disabling exit
* - longjmp_value added to store exit code
* - (optindex < argc) validation added before accessing argv[optindex] inside split_commandline()
* and parse_options()
* - all av_log_set_callback invocations updated to set ffmpegkit_log_callback_function from ffmpegkit.c
* - (optindex < argc) validation added before accessing argv[optindex] inside
* split_commandline() and parse_options()
* - all av_log_set_callback invocations updated to set
* ffmpegkit_log_callback_function from ffmpegkit.c
* - unused log_callback_help method removed
* - (idx + 1 < argc) validation added in parse_loglevel()
*/
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/* Include only the enabled headers since some compilers (namely, Sun
Studio) will not omit unused inline functions and create undefined
references to libraries that are not being built. */
#include "config.h"
#include "ffmpegkit_exception.h"
#include "fftools_cmdutils.h"
#include "fftools_fopen_utf8.h"
#include "fftools_opt_common.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/dict.h"
#include "libavutil/display.h"
#include "libavutil/eval.h"
#include "libavutil/getenv_utf8.h"
#include "libavutil/mathematics.h"
#include "libavutil/imgutils.h"
#include "libavutil/libm.h"
#include "libavutil/parseutils.h"
#include "libavutil/eval.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "fftools_cmdutils.h"
#include "fftools_fopen_utf8.h"
#include "fftools_opt_common.h"
#include "ffmpegkit_exception.h"
#include "libavutil/parseutils.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
#ifdef _WIN32
#include <windows.h>
#include "compat/w32dlfcn.h"
#include <windows.h>
#endif
__thread char *program_name;
@ -110,53 +121,32 @@ __thread AVDictionary *swr_opts;
__thread AVDictionary *format_opts, *codec_opts;
__thread int hide_banner = 0;
__thread int longjmp_value = 0;
void uninit_opts(void)
{
void uninit_opts(void) {
av_dict_free(&swr_opts);
av_dict_free(&sws_dict);
av_dict_free(&format_opts);
av_dict_free(&codec_opts);
}
void init_dynload(void)
{
void init_dynload(void) {
#if HAVE_SETDLLDIRECTORY && defined(_WIN32)
/* Calling SetDllDirectory with the empty string (but not NULL) removes the
* current working directory from the DLL search path as a security pre-caution. */
* current working directory from the DLL search path as a security
* pre-caution. */
SetDllDirectory("");
#endif
}
static __thread void (*program_exit)(int ret);
void register_exit(void (*cb)(int ret))
{
program_exit = cb;
}
void report_and_exit(int ret)
{
av_log(NULL, AV_LOG_FATAL, "%s\n", av_err2str(ret));
exit_program(AVUNERROR(ret));
}
void exit_program(int ret)
{
if (program_exit)
program_exit(ret);
void exit_program(int ret) {
// FFmpegKit
// exit disabled and replaced with longjmp, exit value stored in longjmp_value
// exit disabled and replaced with longjmp, exit value returned by ret
// exit(ret);
longjmp_value = ret;
longjmp(ex_buf__, ret);
}
double parse_number_or_die(const char *context, const char *numstr, int type,
double min, double max)
{
int parse_number(const char *context, const char *numstr, int type, double min,
double max, double *dst) {
char *tail;
const char *error;
double d = av_strtod(numstr, &tail);
@ -168,28 +158,17 @@ double parse_number_or_die(const char *context, const char *numstr, int type,
error = "Expected int64 for %s but found %s\n";
else if (type == OPT_INT && (int)d != d)
error = "Expected int for %s but found %s\n";
else
return d;
av_log(NULL, AV_LOG_FATAL, error, context, numstr, min, max);
exit_program(1);
else {
*dst = d;
return 0;
}
int64_t parse_time_or_die(const char *context, const char *timestr,
int is_duration)
{
int64_t us;
if (av_parse_time(&us, timestr, is_duration) < 0) {
av_log(NULL, AV_LOG_FATAL, "Invalid %s specification for %s: %s\n",
is_duration ? "duration" : "date", context, timestr);
exit_program(1);
}
return us;
av_log(NULL, AV_LOG_FATAL, error, context, numstr, min, max);
return AVERROR(EINVAL);
}
void show_help_options(const OptionDef *options, const char *msg, int req_flags,
int rej_flags, int alt_flags)
{
int rej_flags, int alt_flags) {
const OptionDef *po;
int first;
@ -198,8 +177,7 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags,
char buf[128];
if (((po->flags & req_flags) != req_flags) ||
(alt_flags && !(po->flags & alt_flags)) ||
(po->flags & rej_flags))
(alt_flags && !(po->flags & alt_flags)) || (po->flags & rej_flags))
continue;
if (first) {
@ -216,8 +194,7 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags,
av_log(NULL, AV_LOG_STDERR, "\n");
}
void show_help_children(const AVClass *class, int flags)
{
void show_help_children(const AVClass *class, int flags) {
void *iter = NULL;
const AVClass *child;
if (class->option) {
@ -229,8 +206,7 @@ void show_help_children(const AVClass *class, int flags)
show_help_children(child, flags);
}
static const OptionDef *find_option(const OptionDef *po, const char *name)
{
static const OptionDef *find_option(const OptionDef *po, const char *name) {
while (po->name) {
const char *end;
if (av_strstart(name, po->name, &end) && (!*end || *end == ':'))
@ -256,8 +232,7 @@ static int win32_argc = 0;
* @param argc_ptr Arguments number (including executable)
* @param argv_ptr Arguments list.
*/
static void prepare_app_arguments(int *argc_ptr, char ***argv_ptr)
{
static void prepare_app_arguments(int *argc_ptr, char ***argv_ptr) {
char *argstr_flat;
wchar_t **argv_w;
int i, buffsize = 0, offset = 0;
@ -275,8 +250,8 @@ static void prepare_app_arguments(int *argc_ptr, char ***argv_ptr)
/* determine the UTF-8 buffer size (including NULL-termination symbols) */
for (i = 0; i < win32_argc; i++)
buffsize += WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1,
NULL, 0, NULL, NULL);
buffsize +=
WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL);
win32_argv_utf8 = av_mallocz(sizeof(char *) * (win32_argc + 1) + buffsize);
argstr_flat = (char *)win32_argv_utf8 + sizeof(char *) * (win32_argc + 1);
@ -287,8 +262,8 @@ static void prepare_app_arguments(int *argc_ptr, char ***argv_ptr)
for (i = 0; i < win32_argc; i++) {
win32_argv_utf8[i] = &argstr_flat[offset];
offset += WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1,
&argstr_flat[offset],
offset +=
WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, &argstr_flat[offset],
buffsize - offset, NULL, NULL);
}
win32_argv_utf8[i] = NULL;
@ -298,20 +273,21 @@ static void prepare_app_arguments(int *argc_ptr, char ***argv_ptr)
*argv_ptr = win32_argv_utf8;
}
#else
static inline void prepare_app_arguments(int *argc_ptr, char ***argv_ptr)
{
static inline void prepare_app_arguments(int *argc_ptr, char ***argv_ptr) {
/* nothing to do */
}
#endif /* HAVE_COMMANDLINETOARGVW */
static int write_option(void *optctx, const OptionDef *po, const char *opt,
const char *arg)
{
const char *arg) {
/* new-style options contain an offset into optctx, old-style address of
* a global var*/
void *dst = po->flags & (OPT_OFFSET | OPT_SPEC) ?
(uint8_t *)optctx + po->u.off : po->u.dst_ptr;
void *dst = po->flags & (OPT_OFFSET | OPT_SPEC)
? (uint8_t *)optctx + po->u.off
: po->u.dst_ptr;
int *dstcount;
double num;
int ret;
if (po->flags & OPT_SPEC) {
SpecifierOpt **so = dst;
@ -319,7 +295,10 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt,
char *str;
dstcount = (int *)(so + 1);
*so = grow_array(*so, sizeof(**so), dstcount, *dstcount + 1);
ret = grow_array((void **)so, sizeof(**so), dstcount, *dstcount + 1);
if (ret < 0)
return ret;
str = av_strdup(p ? p + 1 : "");
if (!str)
return AVERROR(ENOMEM);
@ -335,33 +314,54 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt,
return AVERROR(ENOMEM);
*(char **)dst = str;
} else if (po->flags & OPT_BOOL || po->flags & OPT_INT) {
*(int *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX);
ret = parse_number(opt, arg, OPT_INT64, INT_MIN, INT_MAX, &num);
if (ret < 0)
return ret;
*(int *)dst = num;
} else if (po->flags & OPT_INT64) {
*(int64_t *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT64_MIN, (double)INT64_MAX);
ret = parse_number(opt, arg, OPT_INT64, INT64_MIN, (double)INT64_MAX,
&num);
if (ret < 0)
return ret;
*(int64_t *)dst = num;
} else if (po->flags & OPT_TIME) {
*(int64_t *)dst = parse_time_or_die(opt, arg, 1);
ret = av_parse_time(dst, arg, 1);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Invalid duration for option %s: %s\n",
opt, arg);
return ret;
}
} else if (po->flags & OPT_FLOAT) {
*(float *)dst = parse_number_or_die(opt, arg, OPT_FLOAT, -INFINITY, INFINITY);
ret = parse_number(opt, arg, OPT_FLOAT, -INFINITY, INFINITY, &num);
if (ret < 0)
return ret;
*(float *)dst = num;
} else if (po->flags & OPT_DOUBLE) {
*(double *)dst = parse_number_or_die(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY);
ret = parse_number(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY, &num);
if (ret < 0)
return ret;
*(double *)dst = num;
} else if (po->u.func_arg) {
int ret = po->u.func_arg(optctx, opt, arg);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR,
"Failed to set value '%s' for option '%s': %s\n",
arg, opt, av_err2str(ret));
"Failed to set value '%s' for option '%s': %s\n", arg, opt,
av_err2str(ret));
return ret;
}
}
if (po->flags & OPT_EXIT)
exit_program(0);
return AVERROR_EXIT;
return 0;
}
int parse_option(void *optctx, const char *opt, const char *arg,
const OptionDef *options)
{
const OptionDef *options) {
static const OptionDef opt_avoptions = {
.name = "AVOption passthrough",
.flags = HAS_ARG,
@ -398,9 +398,8 @@ int parse_option(void *optctx, const char *opt, const char *arg,
return !!(po->flags & HAS_ARG);
}
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
void (*parse_arg_function)(void *, const char*))
{
int parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
int (*parse_arg_function)(void *, const char *)) {
const char *opt;
int optindex, handleoptions = 1, ret;
@ -420,21 +419,26 @@ void parse_options(void *optctx, int argc, char **argv, const OptionDef *options
opt++;
if (optindex >= argc) {
if ((ret = parse_option(optctx, opt, NULL, options)) < 0)
exit_program(1);
return ret;
} else {
if ((ret = parse_option(optctx, opt, argv[optindex], options)) < 0)
exit_program(1);
if ((ret = parse_option(optctx, opt, argv[optindex], options)) <
0)
return ret;
}
optindex += ret;
} else {
if (parse_arg_function)
parse_arg_function(optctx, opt);
if (parse_arg_function) {
ret = parse_arg_function(optctx, opt);
if (ret < 0)
return ret;
}
}
}
return 0;
}
int parse_optgroup(void *optctx, OptionGroup *g)
{
int parse_optgroup(void *optctx, OptionGroup *g) {
int i, ret;
av_log(NULL, AV_LOG_DEBUG, "Parsing a group of options: %s %s.\n",
@ -443,18 +447,19 @@ int parse_optgroup(void *optctx, OptionGroup *g)
for (i = 0; i < g->nb_opts; i++) {
Option *o = &g->opts[i];
if (g->group_def->flags &&
!(g->group_def->flags & o->opt->flags)) {
av_log(NULL, AV_LOG_ERROR, "Option %s (%s) cannot be applied to "
if (g->group_def->flags && !(g->group_def->flags & o->opt->flags)) {
av_log(NULL, AV_LOG_ERROR,
"Option %s (%s) cannot be applied to "
"%s %s -- you are trying to apply an input option to an "
"output file or vice versa. Move this option before the "
"file it belongs to.\n", o->key, o->opt->help,
g->group_def->name, g->arg);
"file it belongs to.\n",
o->key, o->opt->help, g->group_def->name, g->arg);
return AVERROR(EINVAL);
}
av_log(NULL, AV_LOG_DEBUG, "Applying option %s (%s) with argument %s.\n",
o->key, o->opt->help, o->val);
av_log(NULL, AV_LOG_DEBUG,
"Applying option %s (%s) with argument %s.\n", o->key,
o->opt->help, o->val);
ret = write_option(optctx, o->opt, o->key, o->val);
if (ret < 0)
@ -467,8 +472,7 @@ int parse_optgroup(void *optctx, OptionGroup *g)
}
int locate_option(int argc, char **argv, const OptionDef *options,
const char *optname)
{
const char *optname) {
const OptionDef *po;
int i;
@ -492,8 +496,7 @@ int locate_option(int argc, char **argv, const OptionDef *options,
return 0;
}
static void dump_argument(FILE *report_file, const char *a)
{
static void dump_argument(FILE *report_file, const char *a) {
const unsigned char *p;
for (p = a; *p; p++)
@ -516,8 +519,7 @@ static void dump_argument(FILE *report_file, const char *a)
fputc('"', report_file);
}
static void check_options(const OptionDef *po)
{
static void check_options(const OptionDef *po) {
while (po->name) {
if (po->flags & OPT_PERFILE)
av_assert0(po->flags & (OPT_INPUT | OPT_OUTPUT));
@ -525,8 +527,7 @@ static void check_options(const OptionDef *po)
}
}
void parse_loglevel(int argc, char **argv, const OptionDef *options)
{
void parse_loglevel(int argc, char **argv, const OptionDef *options) {
int idx = locate_option(argc, argv, options, "loglevel");
char *env;
@ -558,17 +559,18 @@ void parse_loglevel(int argc, char **argv, const OptionDef *options)
}
static const AVOption *opt_find(void *obj, const char *name, const char *unit,
int opt_flags, int search_flags)
{
int opt_flags, int search_flags) {
const AVOption *o = av_opt_find(obj, name, unit, opt_flags, search_flags);
if (o && !o->flags)
return NULL;
return o;
}
#define FLAGS (o->type == AV_OPT_TYPE_FLAGS && (arg[0]=='-' || arg[0]=='+')) ? AV_DICT_APPEND : 0
int opt_default(void *optctx, const char *opt, const char *arg)
{
#define FLAGS \
(o->type == AV_OPT_TYPE_FLAGS && (arg[0] == '-' || arg[0] == '+')) \
? AV_DICT_APPEND \
: 0
int opt_default(void *optctx, const char *opt, const char *arg) {
const AVOption *o;
int consumed = 0;
char opt_stripped[128];
@ -599,16 +601,20 @@ int opt_default(void *optctx, const char *opt, const char *arg)
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) {
av_dict_set(&format_opts, opt, arg, FLAGS);
if (consumed)
av_log(NULL, AV_LOG_VERBOSE, "Routing option %s to both codec and muxer layer\n", opt);
av_log(NULL, AV_LOG_VERBOSE,
"Routing option %s to both codec and muxer layer\n", opt);
consumed = 1;
}
#if CONFIG_SWSCALE
if (!consumed && (o = opt_find(&sc, opt, NULL, 0,
if (!consumed &&
(o = opt_find(&sc, opt, NULL, 0,
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) {
if (!strcmp(opt, "srcw") || !strcmp(opt, "srch") ||
!strcmp(opt, "dstw") || !strcmp(opt, "dsth") ||
!strcmp(opt, "src_format") || !strcmp(opt, "dst_format")) {
av_log(NULL, AV_LOG_ERROR, "Directly using swscale dimensions/format options is not supported, please use the -s or -pix_fmt options\n");
av_log(NULL, AV_LOG_ERROR,
"Directly using swscale dimensions/format options is not "
"supported, please use the -s or -pix_fmt options\n");
return AVERROR(EINVAL);
}
av_dict_set(&sws_dict, opt, arg, FLAGS);
@ -617,12 +623,14 @@ int opt_default(void *optctx, const char *opt, const char *arg)
}
#else
if (!consumed && !strcmp(opt, "sws_flags")) {
av_log(NULL, AV_LOG_WARNING, "Ignoring %s %s, due to disabled swscale\n", opt, arg);
av_log(NULL, AV_LOG_WARNING,
"Ignoring %s %s, due to disabled swscale\n", opt, arg);
consumed = 1;
}
#endif
#if CONFIG_SWRESAMPLE
if (!consumed && (o=opt_find(&swr_class, opt, NULL, 0,
if (!consumed &&
(o = opt_find(&swr_class, opt, NULL, 0,
AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) {
av_dict_set(&swr_opts, opt, arg, FLAGS);
consumed = 1;
@ -640,8 +648,7 @@ int opt_default(void *optctx, const char *opt, const char *arg)
* @return index of the group definition that matched or -1 if none
*/
static int match_group_separator(const OptionGroupDef *groups, int nb_groups,
const char *opt)
{
const char *opt) {
int i;
for (i = 0; i < nb_groups; i++) {
@ -659,13 +666,16 @@ static int match_group_separator(const OptionGroupDef *groups, int nb_groups,
* @param group_idx which group definition should this group belong to
* @param arg argument of the group delimiting option
*/
static void finish_group(OptionParseContext *octx, int group_idx,
const char *arg)
{
static int finish_group(OptionParseContext *octx, int group_idx,
const char *arg) {
OptionGroupList *l = &octx->groups[group_idx];
OptionGroup *g;
int ret;
ret = GROW_ARRAY(l->groups, l->nb_groups);
if (ret < 0)
return ret;
GROW_ARRAY(l->groups, l->nb_groups);
g = &l->groups[l->nb_groups - 1];
*g = octx->cur_group;
@ -682,26 +692,32 @@ static void finish_group(OptionParseContext *octx, int group_idx,
swr_opts = NULL;
memset(&octx->cur_group, 0, sizeof(octx->cur_group));
return ret;
}
/*
* Add an option instance to currently parsed group.
*/
static void add_opt(OptionParseContext *octx, const OptionDef *opt,
const char *key, const char *val)
{
static int add_opt(OptionParseContext *octx, const OptionDef *opt,
const char *key, const char *val) {
int global = !(opt->flags & (OPT_PERFILE | OPT_SPEC | OPT_OFFSET));
OptionGroup *g = global ? &octx->global_opts : &octx->cur_group;
int ret;
ret = GROW_ARRAY(g->opts, g->nb_opts);
if (ret < 0)
return ret;
GROW_ARRAY(g->opts, g->nb_opts);
g->opts[g->nb_opts - 1].opt = opt;
g->opts[g->nb_opts - 1].key = key;
g->opts[g->nb_opts - 1].val = val;
return 0;
}
static void init_parse_context(OptionParseContext *octx,
const OptionGroupDef *groups, int nb_groups)
{
static int init_parse_context(OptionParseContext *octx,
const OptionGroupDef *groups, int nb_groups) {
static const OptionGroupDef global_group = {"global"};
int i;
@ -710,17 +726,18 @@ static void init_parse_context(OptionParseContext *octx,
octx->nb_groups = nb_groups;
octx->groups = av_calloc(octx->nb_groups, sizeof(*octx->groups));
if (!octx->groups)
report_and_exit(AVERROR(ENOMEM));
return AVERROR(ENOMEM);
for (i = 0; i < octx->nb_groups; i++)
octx->groups[i].group_def = &groups[i];
octx->global_opts.group_def = &global_group;
octx->global_opts.arg = "";
return 0;
}
void uninit_parse_context(OptionParseContext *octx)
{
void uninit_parse_context(OptionParseContext *octx) {
int i, j;
for (i = 0; i < octx->nb_groups; i++) {
@ -745,16 +762,19 @@ void uninit_parse_context(OptionParseContext *octx)
}
int split_commandline(OptionParseContext *octx, int argc, char *argv[],
const OptionDef *options,
const OptionGroupDef *groups, int nb_groups)
{
const OptionDef *options, const OptionGroupDef *groups,
int nb_groups) {
int ret;
int optindex = 1;
int dashdash = -2;
/* perform system-dependent conversions for arguments list */
prepare_app_arguments(&argc, &argv);
init_parse_context(octx, groups, nb_groups);
ret = init_parse_context(octx, groups, nb_groups);
if (ret < 0)
return ret;
av_log(NULL, AV_LOG_DEBUG, "Splitting the commandline.\n");
while (optindex < argc) {
@ -770,7 +790,10 @@ int split_commandline(OptionParseContext *octx, int argc, char *argv[],
}
/* unnamed group separators, e.g. output filename */
if (opt[0] != '-' || !opt[1] || dashdash + 1 == optindex) {
finish_group(octx, 0, opt);
ret = finish_group(octx, 0, opt);
if (ret < 0)
return ret;
av_log(NULL, AV_LOG_DEBUG, " matched as %s.\n", groups[0].name);
continue;
}
@ -781,7 +804,11 @@ do { \
if (optindex < argc) { \
arg = argv[optindex++]; \
} else { \
av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\n", opt);\
arg = NULL; \
} \
if (!arg) { \
av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\n", \
opt); \
return AVERROR(EINVAL); \
} \
} while (0)
@ -789,7 +816,10 @@ do { \
/* named group separators, e.g. -i */
if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0) {
GET_ARG(arg);
finish_group(octx, ret, arg);
ret = finish_group(octx, ret, arg);
if (ret < 0)
return ret;
av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n",
groups[ret].name, arg);
continue;
@ -811,9 +841,14 @@ do { \
arg = "1";
}
add_opt(octx, po, opt, arg);
av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
"argument '%s'.\n", po->name, po->help, arg);
ret = add_opt(octx, po, opt, arg);
if (ret < 0)
return ret;
av_log(NULL, AV_LOG_DEBUG,
" matched as option '%s' (%s) with "
"argument '%s'.\n",
po->name, po->help, arg);
continue;
}
@ -821,24 +856,33 @@ do { \
if ((optindex < argc) && argv[optindex]) {
ret = opt_default(NULL, opt, argv[optindex]);
if (ret >= 0) {
av_log(NULL, AV_LOG_DEBUG, " matched as AVOption '%s' with "
"argument '%s'.\n", opt, argv[optindex]);
av_log(NULL, AV_LOG_DEBUG,
" matched as AVOption '%s' with "
"argument '%s'.\n",
opt, argv[optindex]);
optindex++;
continue;
} else if (ret != AVERROR_OPTION_NOT_FOUND) {
av_log(NULL, AV_LOG_ERROR, "Error parsing option '%s' "
"with argument '%s'.\n", opt, argv[optindex]);
av_log(NULL, AV_LOG_ERROR,
"Error parsing option '%s' "
"with argument '%s'.\n",
opt, argv[optindex]);
return ret;
}
}
/* boolean -nofoo options */
if (opt[0] == 'n' && opt[1] == 'o' &&
(po = find_option(options, opt + 2)) &&
po->name && po->flags & OPT_BOOL) {
add_opt(octx, po, opt, "0");
av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
"argument 0.\n", po->name, po->help);
(po = find_option(options, opt + 2)) && po->name &&
po->flags & OPT_BOOL) {
ret = add_opt(octx, po, opt, "0");
if (ret < 0)
return ret;
av_log(NULL, AV_LOG_DEBUG,
" matched as option '%s' (%s) with "
"argument 0.\n",
po->name, po->help);
continue;
}
@ -847,7 +891,8 @@ do { \
}
if (octx->cur_group.nb_opts || codec_opts || format_opts)
av_log(NULL, AV_LOG_WARNING, "Trailing option(s) found in the "
av_log(NULL, AV_LOG_WARNING,
"Trailing option(s) found in the "
"command: may be ignored.\n");
av_log(NULL, AV_LOG_DEBUG, "Finished splitting the commandline.\n");
@ -855,13 +900,11 @@ do { \
return 0;
}
void print_error(const char *filename, int err)
{
void print_error(const char *filename, int err) {
av_log(NULL, AV_LOG_ERROR, "%s: %s\n", filename, av_err2str(err));
}
int read_yesno(void)
{
int read_yesno(void) {
int c = getchar();
int yesno = (av_toupper(c) == 'Y');
@ -873,8 +916,7 @@ int read_yesno(void)
FILE *get_preset_file(char *filename, size_t filename_size,
const char *preset_name, int is_path,
const char *codec_name)
{
const char *codec_name) {
FILE *f = NULL;
int i;
#if HAVE_GETMODULEHANDLE && defined(_WIN32)
@ -882,9 +924,11 @@ FILE *get_preset_file(char *filename, size_t filename_size,
#endif
char *env_home = getenv_utf8("HOME");
char *env_ffmpeg_datadir = getenv_utf8("FFMPEG_DATADIR");
const char *base[3] = { env_ffmpeg_datadir,
const char *base[3] = {
env_ffmpeg_datadir,
env_home, /* index=1(HOME) is special: search in a .ffmpeg subfolder */
FFMPEG_DATADIR, };
FFMPEG_DATADIR,
};
if (is_path) {
av_strlcpy(filename, preset_name, filename_size);
@ -898,22 +942,22 @@ FILE *get_preset_file(char *filename, size_t filename_size,
datadir = NULL;
av_free(datadir_w);
if (datadir)
{
if (datadir) {
char *ls;
for (ls = datadir; *ls; ls++)
if (*ls == '\\') *ls = '/';
if (*ls == '\\')
*ls = '/';
if (ls = strrchr(datadir, '/'))
{
if (ls = strrchr(datadir, '/')) {
ptrdiff_t datadir_len = ls - datadir;
size_t desired_size = datadir_len + strlen("/ffpresets") + 1;
char *new_datadir = av_realloc_array(
datadir, desired_size, sizeof *datadir);
char *new_datadir =
av_realloc_array(datadir, desired_size, sizeof *datadir);
if (new_datadir) {
datadir = new_datadir;
datadir[datadir_len] = 0;
strncat(datadir, "/ffpresets", desired_size - 1 - datadir_len);
strncat(datadir, "/ffpresets",
desired_size - 1 - datadir_len);
base[2] = datadir;
}
}
@ -926,8 +970,7 @@ FILE *get_preset_file(char *filename, size_t filename_size,
i != 1 ? "" : "/.ffmpeg", preset_name);
f = fopen_utf8(filename, "r");
if (!f && codec_name) {
snprintf(filename, filename_size,
"%s%s/%s-%s.ffpreset",
snprintf(filename, filename_size, "%s%s/%s-%s.ffpreset",
base[i], i != 1 ? "" : "/.ffmpeg", codec_name,
preset_name);
f = fopen_utf8(filename, "r");
@ -943,21 +986,20 @@ FILE *get_preset_file(char *filename, size_t filename_size,
return f;
}
int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec)
{
int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) {
int ret = avformat_match_stream_specifier(s, st, spec);
if (ret < 0)
av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
return ret;
}
AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, const AVCodec *codec)
{
int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id,
AVFormatContext *s, AVStream *st, const AVCodec *codec,
AVDictionary **dst) {
AVDictionary *ret = NULL;
const AVDictionaryEntry *t = NULL;
int flags = s->oformat ? AV_OPT_FLAG_ENCODING_PARAM
: AV_OPT_FLAG_DECODING_PARAM;
int flags =
s->oformat ? AV_OPT_FLAG_ENCODING_PARAM : AV_OPT_FLAG_DECODING_PARAM;
char prefix = 0;
const AVClass *cc = avcodec_get_class();
@ -985,11 +1027,15 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
char *p = strchr(t->key, ':');
/* check stream specification in opt name */
if (p)
switch (check_stream_specifier(s, st, p + 1)) {
case 1: *p = 0; break;
case 0: continue;
default: exit_program(1);
if (p) {
int err = check_stream_specifier(s, st, p + 1);
if (err < 0) {
av_dict_free(&ret);
return err;
} else if (!err)
continue;
*p = 0;
}
if (av_opt_find(&cc, t->key, NULL, flags, AV_OPT_SEARCH_FAKE_OBJ) ||
@ -1006,55 +1052,67 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
if (p)
*p = ':';
}
return ret;
*dst = ret;
return 0;
}
AVDictionary **setup_find_stream_info_opts(AVFormatContext *s,
AVDictionary *codec_opts)
{
int i;
int setup_find_stream_info_opts(AVFormatContext *s, AVDictionary *codec_opts,
AVDictionary ***dst) {
int ret;
AVDictionary **opts;
*dst = NULL;
if (!s->nb_streams)
return NULL;
return 0;
opts = av_calloc(s->nb_streams, sizeof(*opts));
if (!opts)
report_and_exit(AVERROR(ENOMEM));
for (i = 0; i < s->nb_streams; i++)
opts[i] = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id,
s, s->streams[i], NULL);
return opts;
return AVERROR(ENOMEM);
for (int i = 0; i < s->nb_streams; i++) {
ret = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id,
s, s->streams[i], NULL, &opts[i]);
if (ret < 0)
goto fail;
}
*dst = opts;
return 0;
fail:
for (int i = 0; i < s->nb_streams; i++)
av_dict_free(&opts[i]);
av_freep(&opts);
return ret;
}
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) {
if (new_size >= INT_MAX / elem_size) {
av_log(NULL, AV_LOG_ERROR, "Array too big.\n");
exit_program(1);
return AVERROR(ERANGE);
}
if (*size < new_size) {
uint8_t *tmp = av_realloc_array(array, new_size, elem_size);
uint8_t *tmp = av_realloc_array(*array, new_size, elem_size);
if (!tmp)
report_and_exit(AVERROR(ENOMEM));
return AVERROR(ENOMEM);
memset(tmp + *size * elem_size, 0, (new_size - *size) * elem_size);
*size = new_size;
return tmp;
*array = tmp;
return 0;
}
return array;
return 0;
}
void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems)
{
void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems) {
void *new_elem;
if (!(new_elem = av_mallocz(elem_size)) ||
av_dynarray_add_nofree(ptr, nb_elems, new_elem) < 0)
report_and_exit(AVERROR(ENOMEM));
return NULL;
return new_elem;
}
double get_rotation(int32_t *displaymatrix)
{
double get_rotation(const int32_t *displaymatrix) {
double theta = 0;
if (displaymatrix)
theta = -round(av_display_rotation_get((int32_t *)displaymatrix));
@ -1062,10 +1120,12 @@ double get_rotation(int32_t *displaymatrix)
theta -= 360 * floor(theta / 360 + 0.9 / 360);
if (fabs(theta - 90 * round(theta / 90)) > 2)
av_log(NULL, AV_LOG_WARNING, "Odd rotation angle.\n"
av_log(NULL, AV_LOG_WARNING,
"Odd rotation angle.\n"
"If you want to help, upload a sample "
"of this file to https://streams.videolan.org/upload/ "
"and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)");
"and contact the ffmpeg-devel mailing list. "
"(ffmpeg-devel@ffmpeg.org)");
return theta;
}

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

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
/*
* This file is part of FFmpeg.
* Copyright (c) 2018-2022 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD
* Copyright (c) 2023-2024 ARTHENICA LTD
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,17 +19,22 @@
*/
/*
* This file is the modified version of ffmpeg.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
* This file is the modified version of ffmpeg.h file living in ffmpeg source
* code under the fftools folder. We manually update it each time we depend on a
* new ffmpeg version. Below you can see the list of changes applied by us to
* develop mobile-ffmpeg and later ffmpeg-kit libraries.
*
* ffmpeg-kit changes by ARTHENICA LTD
*
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023
* --------------------------------------------------------
* - FFmpeg 6.0 changes migrated
* - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT, SPECIFIER_OPT_FMT declarations migrated to
* ffmpeg_mux.h
* - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT,
* SPECIFIER_OPT_FMT declarations migrated to ffmpeg_mux.h
* - "class" member field renamed as clazz
* - time field in set_report_callback updated as double
*
@ -39,7 +44,8 @@
* --------------------------------------------------------
* - config.h include added back
* - volatile dropped from thread local variables
* - dropped signatures of ffmpeg_opt.c methods called by both ffmpeg and ffprobe
* - dropped signatures of ffmpeg_opt.c methods called by both ffmpeg and
* ffprobe
*
* 06.2020
* --------------------------------------------------------
@ -47,8 +53,9 @@
*
* 12.2019
* --------------------------------------------------------
* - concurrent execution support ("__thread" specifier added to variables used by multiple threads,
* signatures of ffmpeg_opt.c methods called by both ffmpeg and ffprobe added)
* - concurrent execution support ("__thread" specifier added to variables used
* by multiple threads, signatures of ffmpeg_opt.c methods called by both ffmpeg
* and ffprobe added)
*
* 03.2019
* --------------------------------------------------------
@ -70,10 +77,10 @@
#include "config.h"
#include <signal.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <signal.h>
#include "fftools_cmdutils.h"
#include "fftools_sync_queue.h"
@ -103,6 +110,11 @@
#define FFMPEG_OPT_MAP_CHANNEL 1
#define FFMPEG_OPT_MAP_SYNC 1
#define FFMPEG_ROTATION_METADATA 1
#define FFMPEG_OPT_QPHIST 1
#define FFMPEG_OPT_ADRIFT_THRESHOLD 1
#define FFMPEG_OPT_ENC_TIME_BASE_NUM 1
#define FFMPEG_OPT_TOP 1
#define FFMPEG_OPT_FORCE_KF_SOURCE_NO_DROP 1
enum VideoSyncMethod {
VSYNC_AUTO = -1,
@ -113,7 +125,10 @@ enum VideoSyncMethod {
VSYNC_DROP,
};
#define MAX_STREAMS 1024 /* arbitrary sanity check value */
enum EncTimeBase {
ENC_TIME_BASE_DEMUX = -1,
ENC_TIME_BASE_FILTER = -2,
};
enum HWAccelID {
HWACCEL_NONE = 0,
@ -142,6 +157,12 @@ typedef struct {
} AudioChannelMap;
#endif
typedef struct DemuxPktData {
// estimated dts in AV_TIME_BASE_Q,
// to be used when real dts is missing
int64_t dts_est;
} DemuxPktData;
typedef struct OptionsContext {
OptionGroup *g;
@ -173,6 +194,7 @@ typedef struct OptionsContext {
int loop;
int rate_emu;
float readrate;
double readrate_initial_burst;
int accurate_seek;
int thread_queue_size;
int input_sync_ref;
@ -217,9 +239,8 @@ typedef struct OptionsContext {
int subtitle_disable;
int data_disable;
/* indexed by output file stream index */
int *streamid_map;
int nb_streamid_map;
// keys are stream indices
AVDictionary *streamid;
SpecifierOpt *metadata;
int nb_metadata;
@ -255,8 +276,10 @@ typedef struct OptionsContext {
int nb_inter_matrices;
SpecifierOpt *chroma_intra_matrices;
int nb_chroma_intra_matrices;
#if FFMPEG_OPT_TOP
SpecifierOpt *top_field_first;
int nb_top_field_first;
#endif
SpecifierOpt *metadata_map;
int nb_metadata_map;
SpecifierOpt *presets;
@ -318,62 +341,33 @@ typedef struct OptionsContext {
} OptionsContext;
typedef struct InputFilter {
AVFilterContext *filter;
struct InputStream *ist;
struct FilterGraph *graph;
uint8_t *name;
enum AVMediaType type; // AVMEDIA_TYPE_SUBTITLE for sub2video
AVFifo *frame_queue;
// parameters configured for this input
int format;
int width, height;
AVRational sample_aspect_ratio;
int sample_rate;
AVChannelLayout ch_layout;
AVBufferRef *hw_frames_ctx;
int32_t *displaymatrix;
int eof;
} InputFilter;
typedef struct OutputFilter {
AVFilterContext *filter;
struct OutputStream *ost;
struct FilterGraph *graph;
uint8_t *name;
/* temporary storage until stream maps are processed */
AVFilterInOut *out_tmp;
/* for filters that are not yet bound to an output stream,
* this stores the output linklabel, if any */
uint8_t *linklabel;
enum AVMediaType type;
/* desired output stream properties */
int width, height;
AVRational frame_rate;
int format;
int sample_rate;
AVChannelLayout ch_layout;
// those are only set if no format is specified and the encoder gives us multiple options
// They point directly to the relevant lists of the encoder.
const int *formats;
const AVChannelLayout *ch_layouts;
const int *sample_rates;
/* pts of the last frame received from this filter, in AV_TIME_BASE_Q */
int64_t last_pts;
uint64_t nb_frames_dup;
uint64_t nb_frames_drop;
} OutputFilter;
typedef struct FilterGraph {
const AVClass *clazz;
int index;
const char *graph_desc;
AVFilterGraph *graph;
int reconfiguration;
// true when the filtergraph contains only meta filters
// that do not modify the frame data
int is_meta;
InputFilter **inputs;
int nb_inputs;
@ -381,17 +375,21 @@ typedef struct FilterGraph {
int nb_outputs;
} FilterGraph;
typedef struct Decoder Decoder;
typedef struct InputStream {
const AVClass *clazz;
int file_index;
int index;
AVStream *st;
int discard; /* true if stream data should be discarded */
int user_set_discard;
int decoding_needed; /* non zero if the packets must be decoded in 'raw_fifo', see DECODING_FOR_* */
int decoding_needed; /* non zero if the packets must be decoded in
'raw_fifo', see DECODING_FOR_* */
#define DECODING_FOR_OST 1
#define DECODING_FOR_FILTER 2
int processing_needed; /* non zero if the packets must be processed */
// should attach FrameData as opaque_ref after decoding
int want_frame_data;
/**
* Codec parameters - to be used by the decoding/streamcopy code.
@ -399,65 +397,28 @@ typedef struct InputStream {
* concurrently by the demuxing thread.
*/
AVCodecParameters *par;
Decoder *decoder;
AVCodecContext *dec_ctx;
const AVCodec *dec;
AVFrame *decoded_frame;
AVPacket *pkt;
const AVCodecDescriptor *codec_desc;
AVRational framerate_guessed;
int64_t prev_pkt_pts;
int64_t start; /* time when read started */
/* predicted dts of the next packet read for this stream or (when there are
* several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */
int64_t next_dts;
int64_t first_dts; ///< dts of the first packet read for this stream (in AV_TIME_BASE units)
int64_t dts; ///< dts of the last packet read for this stream (in AV_TIME_BASE units)
int64_t next_pts; ///< synthetic pts for the next decode frame (in AV_TIME_BASE units)
int64_t pts; ///< current pts of the decoded frame (in AV_TIME_BASE units)
int wrap_correction_done;
// the value of AVCodecParserContext.repeat_pict from the AVStream parser
// for the last packet returned from ifile_get_packet()
// -1 if unknown
// FIXME: this is a hack, the avstream parser should not be used
int last_pkt_repeat_pict;
int64_t filter_in_rescale_delta_last;
int64_t min_pts; /* pts with the smallest value in a current stream */
int64_t max_pts; /* pts with the higher value in a current stream */
// when forcing constant input framerate through -r,
// this contains the pts that will be given to the next decoded frame
int64_t cfr_next_pts;
int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */
int64_t nb_samples; /* number of samples in the last decoded audio frame
before looping */
double ts_scale;
int saw_first_ts;
AVDictionary *decoder_opts;
AVRational framerate; /* framerate forced with -r */
#if FFMPEG_OPT_TOP
int top_field_first;
int guess_layout_max;
#endif
int autorotate;
int fix_sub_duration;
struct { /* previous decoded subtitle and related variables */
int got_output;
int ret;
AVSubtitle subtitle;
} prev_sub;
struct sub2video {
int64_t last_pts;
int64_t end_pts;
AVFifo *sub_queue; ///< queue of AVSubtitle* before filter init
AVFrame *frame;
int w, h;
unsigned int initialize; ///< marks if sub2video_update should force an initialization
} sub2video;
/* decoded data from this stream goes into all those filters
@ -465,6 +426,14 @@ typedef struct InputStream {
InputFilter **filters;
int nb_filters;
/*
* Output targets that do not go through lavfi, i.e. subtitles or
* streamcopy. Those two cases are distinguished by the OutputStream
* having an encoder or not.
*/
struct OutputStream **outputs;
int nb_outputs;
int reinit_filters;
/* hwaccel options */
@ -473,22 +442,11 @@ typedef struct InputStream {
char *hwaccel_device;
enum AVPixelFormat hwaccel_output_format;
int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame);
enum AVPixelFormat hwaccel_pix_fmt;
/* stats */
// combined size of all the packets read
uint64_t data_size;
/* number of packets successfully read for this stream */
uint64_t nb_packets;
// number of frames/samples retrieved from the decoder
uint64_t frames_decoded;
uint64_t samples_decoded;
int64_t *dts_buffer;
int nb_dts_buffer;
int got_output;
uint64_t decode_errors;
} InputStream;
typedef struct LastFrameDuration {
@ -497,8 +455,13 @@ typedef struct LastFrameDuration {
} LastFrameDuration;
typedef struct InputFile {
const AVClass *clazz;
int index;
// input format has no timestamps
int format_nots;
AVFormatContext *ctx;
int eof_reached; /* true if eof reached */
int eagain; /* true if last read attempt returned EAGAIN */
@ -509,12 +472,8 @@ typedef struct InputFile {
*/
int64_t start_time_effective;
int64_t ts_offset;
/**
* Extra timestamp offset added by discontinuity handling.
*/
int64_t ts_offset_discont;
int64_t last_ts;
int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */
int64_t start_time; /* user-specified start time in AV_TIME_BASE or
AV_NOPTS_VALUE */
int64_t recording_time;
/* streams that ffmpeg is aware of;
@ -523,7 +482,6 @@ typedef struct InputFile {
InputStream **streams;
int nb_streams;
int rate_emu;
float readrate;
int accurate_seek;
@ -589,7 +547,9 @@ typedef enum {
enum {
KF_FORCE_SOURCE = 1,
#if FFMPEG_OPT_FORCE_KF_SOURCE_NO_DROP
KF_FORCE_SOURCE_NO_DROP = 2,
#endif
};
typedef struct KeyframeForceCtx {
@ -608,44 +568,35 @@ typedef struct KeyframeForceCtx {
int dropped_keyframe;
} KeyframeForceCtx;
typedef struct Encoder Encoder;
typedef struct OutputStream {
const AVClass *clazz;
enum AVMediaType type;
int file_index; /* file index */
int index; /* stream index in the output file */
/**
* Codec parameters for packets submitted to the muxer (i.e. before
* bitstream filtering, if any).
*/
AVCodecParameters *par_in;
/* input stream that is the source for this output stream;
* may be NULL for streams with no well-defined source, e.g.
* attachments or outputs from complex filtergraphs */
InputStream *ist;
AVStream *st; /* stream in the output file */
/* number of frames emitted by the video-encoding sync code */
int64_t vsync_frame_number;
/* predicted pts of the next frame to be encoded
* audio/video encoding only */
int64_t next_pts;
/* dts of the last packet sent to the muxing queue, in AV_TIME_BASE_Q */
int64_t last_mux_dts;
/* pts of the last frame received from the filters, in AV_TIME_BASE_Q */
int64_t last_filter_pts;
// timestamp from which the streamcopied streams should start,
// in AV_TIME_BASE_Q;
// everything before it should be discarded
int64_t ts_copy_start;
// the timebase of the packets sent to the muxer
AVRational mux_timebase;
AVRational enc_timebase;
Encoder *enc;
AVCodecContext *enc_ctx;
AVFrame *filtered_frame;
AVFrame *last_frame;
AVFrame *sq_frame;
AVPacket *pkt;
int64_t last_dropped;
int64_t last_nb0_frames[3];
/* video only */
AVRational frame_rate;
@ -653,7 +604,9 @@ typedef struct OutputStream {
enum VideoSyncMethod vsync_method;
int is_cfr;
int force_fps;
#if FFMPEG_OPT_TOP
int top_field_first;
#endif
#if FFMPEG_ROTATION_METADATA
int rotate_overridden;
#endif
@ -670,7 +623,8 @@ typedef struct OutputStream {
/* audio only */
#if FFMPEG_OPT_MAP_CHANNEL
int *audio_channels_map; /* list of the channels id to pick from the source stream */
int *audio_channels_map; /* list of the channels id to pick from the source
stream */
int audio_channels_mapped; /* number of channels in audio_channels_map */
#endif
@ -678,53 +632,35 @@ typedef struct OutputStream {
FILE *logfile;
OutputFilter *filter;
char *avfilter;
char *filters; ///< filtergraph associated to the -filter option
char *filters_script; ///< filtergraph script associated to the -filter_script option
AVDictionary *encoder_opts;
AVDictionary *sws_dict;
AVDictionary *swr_opts;
char *apad;
OSTFinished finished; /* no more packets should be written for this stream */
int unavailable; /* true if the steram is unavailable (possibly temporarily) */
OSTFinished
finished; /* no more packets should be written for this stream */
int unavailable; /* true if the steram is unavailable (possibly temporarily)
*/
// init_output_stream() has been called for this stream
// The encoder and the bitstream filters have been initialized and the stream
// parameters are set in the AVStream.
// The encoder and the bitstream filters have been initialized and the
// stream parameters are set in the AVStream.
int initialized;
int inputs_done;
const char *attachment_filename;
int streamcopy_started;
int copy_initial_nonkeyframes;
int copy_prior_start;
int keep_pix_fmt;
/* stats */
// combined size of all the packets sent to the muxer
uint64_t data_size_mux;
// combined size of all the packets received from the encoder
uint64_t data_size_enc;
// number of packets send to the muxer
atomic_uint_least64_t packets_written;
// number of frames/samples sent to the encoder
uint64_t frames_encoded;
uint64_t samples_encoded;
// number of packets received from the encoder
uint64_t packets_encoded;
/* packet quality factor */
int quality;
/* packet picture type */
int pict_type;
/* frame encode sum of squared error values */
int64_t error[4];
int sq_idx_encode;
int sq_idx_mux;
@ -751,13 +687,29 @@ typedef struct OutputFile {
SyncQueue *sq_encode;
int64_t recording_time; ///< desired length of the resulting file in microseconds == AV_TIME_BASE units
int64_t recording_time; ///< desired length of the resulting file in
///< microseconds == AV_TIME_BASE units
int64_t start_time; ///< start time in microseconds == AV_TIME_BASE units
int shortest;
int bitexact;
} OutputFile;
// optionally attached as opaque_ref to decoded AVFrames
typedef struct FrameData {
// properties that come from the decoder
struct {
uint64_t frame_num;
int64_t pts;
AVRational tb;
} dec;
AVRational frame_rate_filter;
int bits_per_raw_sample;
} FrameData;
extern __thread InputFile **input_files;
extern __thread int nb_input_files;
@ -770,7 +722,6 @@ extern __thread int nb_filtergraphs;
extern __thread char *vstats_filename;
extern __thread char *sdp_filename;
extern __thread float audio_drift_threshold;
extern __thread float dts_delta_threshold;
extern __thread float dts_error_threshold;
@ -788,7 +739,6 @@ extern __thread int exit_on_error;
extern __thread int abort_on_flags;
extern __thread int print_stats;
extern __thread int64_t stats_period;
extern __thread int qp_hist;
extern __thread int stdin_interaction;
extern __thread AVIOContext *progress_avio;
extern __thread float max_error_rate;
@ -810,6 +760,8 @@ extern __thread int copy_unknown_streams;
extern __thread int recast_media;
extern __thread FILE *vstats_file;
#if FFMPEG_OPT_PSNR
extern __thread int do_psnr;
#endif
@ -820,40 +772,120 @@ void term_exit(void);
void show_usage(void);
void remove_avoptions(AVDictionary **a, AVDictionary *b);
void assert_avoptions(AVDictionary *m);
int check_avoptions(AVDictionary *m);
void assert_file_overwrite(const char *filename);
int assert_file_overwrite(const char *filename);
char *file_read(const char *filename);
AVDictionary *strip_specifiers(const AVDictionary *dict);
const AVCodec *find_codec_or_die(void *logctx, const char *name,
enum AVMediaType type, int encoder);
int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_idx, int is_global);
int configure_filtergraph(FilterGraph *fg);
void check_filter_outputs(void);
int filtergraph_is_simple(FilterGraph *fg);
int init_simple_filtergraph(InputStream *ist, OutputStream *ost);
int find_codec(void *logctx, const char *name, enum AVMediaType type,
int encoder, const AVCodec **codec);
int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx,
int st_idx, int is_global);
int check_filter_outputs(void);
int filtergraph_is_simple(const FilterGraph *fg);
int init_simple_filtergraph(InputStream *ist, OutputStream *ost,
char *graph_desc);
int init_complex_filtergraph(FilterGraph *fg);
void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub);
int copy_av_subtitle(AVSubtitle *dst, const AVSubtitle *src);
int subtitle_wrap_frame(AVFrame *frame, AVSubtitle *subtitle, int copy);
/**
* Get our axiliary frame data attached to the frame, allocating it
* if needed.
*/
FrameData *frame_data(AVFrame *frame);
int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame);
int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame,
int keep_reference);
int ifilter_send_eof(InputFilter *ifilter, int64_t pts, AVRational tb);
int ifilter_sub2video(InputFilter *ifilter, const AVFrame *frame);
void ifilter_sub2video_heartbeat(InputFilter *ifilter, int64_t pts,
AVRational tb);
/**
* Set up fallback filtering parameters from a decoder context. They will only
* be used if no frames are ever sent on this input, otherwise the actual
* parameters are taken from the frame.
*/
int ifilter_parameters_from_dec(InputFilter *ifilter,
const AVCodecContext *dec);
int ofilter_bind_ost(OutputFilter *ofilter, OutputStream *ost);
/**
* Create a new filtergraph in the global filtergraph list.
*
* @param graph_desc Graph description; an av_malloc()ed string, filtergraph
* takes ownership of it.
*/
int fg_create(FilterGraph **pfg, char *graph_desc);
void fg_free(FilterGraph **pfg);
/**
* Perform a step of transcoding for the specified filter graph.
*
* @param[in] graph filter graph to consider
* @param[out] best_ist input stream where a frame would allow to continue
* @return 0 for success, <0 for error
*/
int fg_transcode_step(FilterGraph *graph, InputStream **best_ist);
void fg_send_command(FilterGraph *fg, double time, const char *target,
const char *command, const char *arg, int all_filters);
/**
* Get and encode new output from specified filtergraph, without causing
* activity.
*
* @return 0 for success, <0 for severe errors
*/
int reap_filters(FilterGraph *fg, int flush);
int ffmpeg_parse_options(int argc, char **argv);
void enc_stats_write(OutputStream *ost, EncStats *es,
const AVFrame *frame, const AVPacket *pkt,
uint64_t frame_num);
void enc_stats_write(OutputStream *ost, EncStats *es, const AVFrame *frame,
const AVPacket *pkt, uint64_t frame_num);
HWDevice *hw_device_get_by_name(const char *name);
HWDevice *hw_device_get_by_type(enum AVHWDeviceType type);
int hw_device_init_from_string(const char *arg, HWDevice **dev);
int hw_device_init_from_type(enum AVHWDeviceType type, const char *device,
HWDevice **dev_out);
void hw_device_free_all(void);
int hw_device_setup_for_decode(InputStream *ist);
int hw_device_setup_for_encode(OutputStream *ost);
int hw_device_setup_for_filter(FilterGraph *fg);
/**
* Get a hardware device to be used with this filtergraph.
* The returned reference is owned by the callee, the caller
* must ref it explicitly for long-term use.
*/
AVBufferRef *hw_device_for_filter(void);
int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input);
int hwaccel_decode_init(AVCodecContext *avctx);
int dec_open(InputStream *ist);
void dec_free(Decoder **pdec);
/**
* Submit a packet for decoding
*
* When pkt==NULL and no_eof=0, there will be no more input. Flush decoders and
* mark all downstreams as finished.
*
* When pkt==NULL and no_eof=1, the stream was reset (e.g. after a seek). Flush
* decoders and await further input.
*/
int dec_packet(InputStream *ist, const AVPacket *pkt, int no_eof);
int enc_alloc(Encoder **penc, const AVCodec *codec);
void enc_free(Encoder **penc);
int enc_open(OutputStream *ost, const AVFrame *frame);
int enc_subtitle(OutputFile *of, OutputStream *ost, const AVSubtitle *sub);
int enc_frame(OutputStream *ost, AVFrame *frame);
int enc_flush(void);
/*
* Initialize muxing state for the given stream, should be called
@ -864,22 +896,17 @@ int hwaccel_decode_init(AVCodecContext *avctx);
int of_stream_init(OutputFile *of, OutputStream *ost);
int of_write_trailer(OutputFile *of);
int of_open(const OptionsContext *o, const char *filename);
void of_close(OutputFile **pof);
void of_free(OutputFile **pof);
void of_enc_stats_close(void);
/*
* Send a single packet to the output, applying any bitstream filters
* associated with the output stream. This may result in any number
* of packets actually being written, depending on what bitstream
* filters are applied. The supplied packet is consumed and will be
* blank (as if newly-allocated) when this function returns.
*
* If eof is set, instead indicate EOF to all bitstream filters and
* therefore flush any delayed packets to the output. A blank packet
* must be supplied in this case.
int of_output_packet(OutputFile *of, OutputStream *ost, AVPacket *pkt);
/**
* @param dts predicted packet dts in AV_TIME_BASE_Q
*/
void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof);
int of_streamcopy(OutputStream *ost, const AVPacket *pkt, int64_t dts);
int64_t of_filesize(OutputFile *of);
int ifile_open(const OptionsContext *o, const char *filename);
@ -897,16 +924,66 @@ void ifile_close(InputFile **f);
*/
int ifile_get_packet(InputFile *f, AVPacket **pkt);
int ist_output_add(InputStream *ist, OutputStream *ost);
int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple);
/**
* Find an unused input stream of given type.
*/
InputStream *ist_find_unused(enum AVMediaType type);
/* iterate over all input streams in all input files;
* pass NULL to start iteration */
InputStream *ist_iter(InputStream *prev);
/* iterate over all output streams in all output files;
* pass NULL to start iteration */
OutputStream *ost_iter(OutputStream *prev);
void close_output_stream(OutputStream *ost);
int trigger_fix_sub_duration_heartbeat(OutputStream *ost, const AVPacket *pkt);
int fix_sub_duration_heartbeat(InputStream *ist, int64_t signal_pts);
void update_benchmark(const char *fmt, ...);
/**
* Merge two return codes - return one of the error codes if at least one of
* them was negative, 0 otherwise.
*/
static inline int err_merge(int err0, int err1) {
// prefer "real" errors over EOF
if ((err0 >= 0 || err0 == AVERROR_EOF) && err1 < 0)
return err1;
return (err0 < 0) ? err0 : FFMIN(err1, 0);
}
extern const char *const opt_name_codec_names[];
extern const char *const opt_name_codec_tags[];
extern const char *const opt_name_frame_rates[];
#if FFMPEG_OPT_TOP
extern const char *const opt_name_top_field_first[];
#endif
static inline void pkt_move(void *dst, void *src) {
av_packet_move_ref(dst, src);
}
static inline void frame_move(void *dst, void *src) {
av_frame_move_ref(dst, src);
}
typedef struct BenchmarkTimeStamps {
int64_t real_usec;
int64_t user_usec;
int64_t sys_usec;
} BenchmarkTimeStamps;
typedef struct EncStatsFile {
char *path;
AVIOContext *io;
} EncStatsFile;
void set_report_callback(void (*callback)(int, float, float, int64_t, double, double, double));
void set_report_callback(void (*callback)(int, float, float, int64_t, double,
double, double));
void cancel_operation(long id);
#endif /* FFTOOLS_FFMPEG_H */

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

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

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

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

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

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

@ -18,9 +18,10 @@
*/
/*
* This file is the modified version of objpool.h file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
* This file is the modified version of objpool.h file living in ffmpeg source
* code under the fftools folder. We manually update it each time we depend on a
* new ffmpeg version. Below you can see the list of changes applied by us to
* develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
*

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

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

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

@ -18,8 +18,9 @@
*/
/*
* This file is the modified version of thread_queue.c file living in ffmpeg source code under the fftools folder. We
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
* This file is the modified version of thread_queue.c file living in ffmpeg
* source code under the fftools folder. We manually update it each time we
* depend on a new ffmpeg version. Below you can see the list of changes applied
* by us to develop ffmpeg-kit library.
*
* ffmpeg-kit changes by ARTHENICA LTD
@ -66,8 +67,7 @@ struct ThreadQueue {
pthread_cond_t cond;
};
void tq_free(ThreadQueue **ptq)
{
void tq_free(ThreadQueue **ptq) {
ThreadQueue *tq = *ptq;
if (!tq)
@ -91,8 +91,8 @@ void tq_free(ThreadQueue **ptq)
}
ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size,
ObjPool *obj_pool, void (*obj_move)(void *dst, void *src))
{
ObjPool *obj_pool,
void (*obj_move)(void *dst, void *src)) {
ThreadQueue *tq;
int ret;
@ -131,8 +131,7 @@ fail:
return NULL;
}
int tq_send(ThreadQueue *tq, unsigned int stream_idx, void *data)
{
int tq_send(ThreadQueue *tq, unsigned int stream_idx, void *data) {
int *finished;
int ret;
@ -172,9 +171,7 @@ finish:
return ret;
}
static int receive_locked(ThreadQueue *tq, int *stream_idx,
void *data)
{
static int receive_locked(ThreadQueue *tq, int *stream_idx, void *data) {
FifoElem elem;
unsigned int nb_finished = 0;
@ -202,8 +199,7 @@ static int receive_locked(ThreadQueue *tq, int *stream_idx,
return nb_finished == tq->nb_streams ? AVERROR_EOF : AVERROR(EAGAIN);
}
int tq_receive(ThreadQueue *tq, int *stream_idx, void *data)
{
int tq_receive(ThreadQueue *tq, int *stream_idx, void *data) {
int ret;
*stream_idx = -1;
@ -228,8 +224,7 @@ int tq_receive(ThreadQueue *tq, int *stream_idx, void *data)
return ret;
}
void tq_send_finish(ThreadQueue *tq, unsigned int stream_idx)
{
void tq_send_finish(ThreadQueue *tq, unsigned int stream_idx) {
av_assert0(stream_idx < tq->nb_streams);
pthread_mutex_lock(&tq->lock);
@ -243,8 +238,7 @@ void tq_send_finish(ThreadQueue *tq, unsigned int stream_idx)
pthread_mutex_unlock(&tq->lock);
}
void tq_receive_finish(ThreadQueue *tq, unsigned int stream_idx)
{
void tq_receive_finish(ThreadQueue *tq, unsigned int stream_idx) {
av_assert0(stream_idx < tq->nb_streams);
pthread_mutex_lock(&tq->lock);

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

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

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

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

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

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

Binary file not shown.

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

281
android/gradlew vendored

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

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

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

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

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

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

10
apple/.gitignore vendored

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

@ -38,7 +38,7 @@ PROJECT_NAME = "FFmpegKit iOS / macOS / tvOS API"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 6.0
PROJECT_NUMBER = 6.1.2
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

1251
apple/aclocal.m4 vendored

File diff suppressed because it is too large Load Diff

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

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

1748
apple/config.guess vendored

File diff suppressed because it is too large Load Diff

1884
apple/config.sub vendored

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

@ -49,7 +49,9 @@ extern void addSessionToSessionHistory(id<Session> session);
sessionIdGenerator = [[AtomicLong alloc] initWithValue:1];
}
- (instancetype)init:(NSArray*)arguments withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
- (instancetype)init:(NSArray *)arguments
withLogCallback:(LogCallback)logCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
self = [super init];
if (self) {
_sessionId = [sessionIdGenerator getAndIncrement];
@ -95,7 +97,9 @@ extern void addSessionToSessionHistory(id<Session> session);
NSDate *startTime = _startTime;
NSDate *endTime = _endTime;
if (startTime != nil && endTime != nil) {
return [[NSNumber numberWithDouble:([endTime timeIntervalSinceDate:startTime]*1000)] longValue];
return [[NSNumber
numberWithDouble:([endTime timeIntervalSinceDate:startTime] * 1000)]
longValue];
}
return 0;
@ -110,11 +114,14 @@ extern void addSessionToSessionHistory(id<Session> session);
}
- (void)waitForAsynchronousMessagesInTransmit:(int)timeout {
NSDate* expireDate = [[NSDate date] dateByAddingTimeInterval:((double)timeout)/1000];
NSDate *expireDate =
[[NSDate date] dateByAddingTimeInterval:((double)timeout) / 1000];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
while ([self thereAreAsynchronousMessagesInTransmit] && ([[NSDate date] timeIntervalSinceDate:expireDate] < 0)) {
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC));
while ([self thereAreAsynchronousMessagesInTransmit] &&
([[NSDate date] timeIntervalSinceDate:expireDate] < 0)) {
dispatch_semaphore_wait(
semaphore, dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC));
}
}
@ -122,14 +129,19 @@ extern void addSessionToSessionHistory(id<Session> session);
[self waitForAsynchronousMessagesInTransmit:waitTimeout];
if ([self thereAreAsynchronousMessagesInTransmit]) {
NSLog(@"getAllLogsWithTimeout was called to return all logs but there are still logs being transmitted for session id %ld.", _sessionId);
NSLog(@"getAllLogsWithTimeout was called to return all logs but there "
@"are "
@"still logs being transmitted for session id %ld.",
_sessionId);
}
return [self getLogs];
}
- (NSArray *)getAllLogs {
return [self getAllLogsWithTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return [self
getAllLogsWithTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
}
- (NSArray *)getLogs {
@ -144,14 +156,19 @@ extern void addSessionToSessionHistory(id<Session> session);
[self waitForAsynchronousMessagesInTransmit:waitTimeout];
if ([self thereAreAsynchronousMessagesInTransmit]) {
NSLog(@"getAllLogsAsStringWithTimeout was called to return all logs but there are still logs being transmitted for session id %ld.", _sessionId);
NSLog(
@"getAllLogsAsStringWithTimeout was called to return all logs but "
@"there are still logs being transmitted for session id %ld.",
_sessionId);
}
return [self getLogsAsString];
}
- (NSString *)getAllLogsAsString {
return [self getAllLogsAsStringWithTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return [self
getAllLogsAsStringWithTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
}
- (NSString *)getLogsAsString {
@ -208,7 +225,9 @@ extern void addSessionToSessionHistory(id<Session> session);
}
- (void)fail:(NSException *)exception {
_failStackTrace = [NSString stringWithFormat:@"%@\n%@", [exception userInfo], [exception callStackSymbols]];
_failStackTrace =
[NSString stringWithFormat:@"%@\n%@", [exception userInfo],
[exception callStackSymbols]];
_state = SessionStateFailed;
_endTime = [NSDate date];
}

@ -41,6 +41,13 @@
*/
+ (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
#endif // FFMPEG_KIT_ARCH_DETECT_H

@ -17,13 +17,13 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/
#import <sys/types.h>
#import <sys/sysctl.h>
#import <mach/machine.h>
#import "ArchDetect.h"
#import "FFmpegKitConfig.h"
#import "FFmpegKit.h"
#import "FFmpegKitConfig.h"
#import "FFprobeKit.h"
#import <mach/machine.h>
#import <sys/sysctl.h>
#import <sys/types.h>
@implementation ArchDetect
@ -73,12 +73,12 @@
[cpu appendString:@"v8"];
break;
#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
case CPU_SUBTYPE_ARM64E:
[cpu appendString:@"e"];
break;
#endif
}
} else if (type == CPU_TYPE_ARM) {
@ -125,7 +125,8 @@
#endif
}
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120100
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= 120100
} else if (type == CPU_TYPE_ARM64_32) {
[cpu appendString:@"arm64_32"];
@ -169,4 +170,12 @@
return arch;
}
+ (NSString *)getMinSdk {
#ifdef FFMPEG_KIT_MIN_SDK
return [NSString stringWithUTF8String:FFMPEG_KIT_MIN_SDK];
#else
return @"";
#endif
}
@end

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

@ -75,7 +75,8 @@ extern NSString* const ChapterKeyTags;
/**
* 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;

@ -33,7 +33,6 @@ NSString* const ChapterKeyTags = @"tags";
* Stores all properties.
*/
NSDictionary *dictionary;
}
- (instancetype)init:(NSDictionary *)chapterDictionary {

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

@ -31,7 +31,19 @@
[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 {
@ -40,56 +52,102 @@
return session;
}
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback];
+ (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
+ (FFmpegSession *)
executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback];
+ (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session;
}
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
+ (FFmpegSession *)
executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session;
}
+ (FFmpegSession *)execute:(NSString *)command {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command]];
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig ffmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback];
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session];
return session;
}
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback];
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session;
}
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback];
+ (FFmpegSession *)executeAsync:(NSString *)command
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session;
}
@ -98,7 +156,8 @@
/*
* ZERO (0) IS A SPECIAL SESSION ID
* WHEN IT IS PASSED TO THIS METHOD, A SIGINT IS GENERATED WHICH CANCELS ALL ONGOING SESSIONS
* WHEN IT IS PASSED TO THIS METHOD, A SIGINT IS GENERATED WHICH CANCELS ALL
* ONGOING SESSIONS
*/
cancel_operation(0);
}

@ -20,15 +20,15 @@
#ifndef FFMPEG_KIT_CONFIG_H
#define FFMPEG_KIT_CONFIG_H
#import <stdio.h>
#import <pthread.h>
#import <unistd.h>
#import <Foundation/Foundation.h>
#import "FFmpegSession.h"
#import "FFprobeSession.h"
#import "LogCallback.h"
#import "MediaInformationSession.h"
#import "StatisticsCallback.h"
#import <Foundation/Foundation.h>
#import <pthread.h>
#import <stdio.h>
#import <unistd.h>
/** Global library version */
extern NSString *const FFmpegKitVersion;
@ -42,29 +42,31 @@ typedef NS_ENUM(NSUInteger, Signal) {
};
/**
* <p>Configuration class of <code>FFmpegKit</code> library. Allows customizing the global library
* options. Provides helper methods to support additional resources.
* <p>Configuration class of <code>FFmpegKit</code> library. Allows customizing
* the global library options. Provides helper methods to support additional
* resources.
*/
@interface FFmpegKitConfig : NSObject
/**
* <p>Enables log and statistics redirection.
*
* <p>When redirection is enabled FFmpeg/FFprobe logs are redirected to NSLog and sessions
* collect log and statistics entries for the executions. It is possible to define global or
* session specific log/statistics callbacks as well.
* <p>When redirection is enabled FFmpeg/FFprobe logs are redirected to NSLog
* and sessions collect log and statistics entries for the executions. It is
* possible to define global or session specific log/statistics callbacks as
* well.
*
* <p>Note that redirection is enabled by default. If you do not want to use its functionality
* please use disableRedirection method to disable it.
* <p>Note that redirection is enabled by default. If you do not want to use its
* functionality please use disableRedirection method to disable it.
*/
+ (void)enableRedirection;
/**
* <p>Disables log and statistics redirection.
*
* <p>When redirection is disabled logs are printed to stderr, all logs and statistics
* callbacks are disabled and <code>FFprobe</code>'s <code>getMediaInformation</code> methods
* do not work.
* <p>When redirection is disabled logs are printed to stderr, all logs and
* statistics callbacks are disabled and <code>FFprobe</code>'s
* <code>getMediaInformation</code> methods do not work.
*/
+ (void)disableRedirection;
@ -77,32 +79,37 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (int)setFontconfigConfigurationPath:(NSString *)path;
/**
* <p>Registers the fonts inside the given path, so they become available to use in FFmpeg
* filters.
* <p>Registers the fonts inside the given path, so they become available to use
* in FFmpeg filters.
*
* <p>Note that you need to build <code>FFmpegKit</code> with <code>fontconfig</code>
* enabled or use a prebuilt package with <code>fontconfig</code> inside to be able to use
* fonts in <code>FFmpeg</code>.
* <p>Note that you need to build <code>FFmpegKit</code> with
* <code>fontconfig</code> enabled or use a prebuilt package with
* <code>fontconfig</code> inside to be able to use fonts in
* <code>FFmpeg</code>.
*
* @param fontDirectoryPath directory that contains fonts (.ttf and .otf files)
* @param fontNameMapping custom font name mappings, useful to access your fonts with more
* friendly names
* @param fontNameMapping custom font name mappings, useful to access your
* fonts with more friendly names
*/
+ (void)setFontDirectory:(NSString*)fontDirectoryPath with:(NSDictionary*)fontNameMapping;
+ (void)setFontDirectory:(NSString *)fontDirectoryPath
with:(NSDictionary *)fontNameMapping;
/**
* <p>Registers the fonts inside the given array of font directories, so they become available
* to use in FFmpeg filters.
* <p>Registers the fonts inside the given array of font directories, so they
* become available to use in FFmpeg filters.
*
* <p>Note that you need to build <code>FFmpegKit</code> with <code>fontconfig</code>
* enabled or use a prebuilt package with <code>fontconfig</code> inside to be able to use
* fonts in <code>FFmpeg</code>.
* <p>Note that you need to build <code>FFmpegKit</code> with
* <code>fontconfig</code> enabled or use a prebuilt package with
* <code>fontconfig</code> inside to be able to use fonts in
* <code>FFmpeg</code>.
*
* @param fontDirectoryList array of directories that contain fonts (.ttf and .otf files)
* @param fontNameMapping custom font name mappings, useful to access your fonts with more
* friendly names
* @param fontDirectoryList array of directories that contain fonts (.ttf and
* .otf files)
* @param fontNameMapping custom font name mappings, useful to access your
* fonts with more friendly names
*/
+ (void)setFontDirectoryList:(NSArray*)fontDirectoryList with:(NSDictionary*)fontNameMapping;
+ (void)setFontDirectoryList:(NSArray *)fontDirectoryList
with:(NSDictionary *)fontNameMapping;
/**
* <p>Creates a new named pipe to use in <code>FFmpeg</code> operations.
@ -121,7 +128,8 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (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
*/
@ -139,7 +147,7 @@ typedef NS_ENUM(NSUInteger, Signal) {
*
* @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.
@ -155,11 +163,12 @@ typedef NS_ENUM(NSUInteger, Signal) {
* @param variableValue environment variable value
* @return zero on success, non-zero on error
*/
+ (int)setEnvironmentVariable:(NSString*)variableName value:(NSString*)variableValue;
+ (int)setEnvironmentVariable:(NSString *)variableName
value:(NSString *)variableValue;
/**
* <p>Registers a new ignored signal. Ignored signals are not handled by <code>FFmpegKit</code>
* library.
* <p>Registers a new ignored signal. Ignored signals are not handled by
* <code>FFmpegKit</code> library.
*
* @param signal signal to be ignored
*/
@ -175,23 +184,29 @@ typedef NS_ENUM(NSUInteger, Signal) {
/**
* <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;
/**
* <p>Synchronously executes the media information session provided.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param waitTimeout max time to wait until media information is transmitted
* @param mediaInformationSession media information session which includes
* command options/arguments
* @param waitTimeout max time to wait until media information is
* transmitted
*/
+ (void)getMediaInformationExecute:(MediaInformationSession*)mediaInformationSession withTimeout:(int)waitTimeout;
+ (void)getMediaInformationExecute:
(MediaInformationSession *)mediaInformationSession
withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFmpeg execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
*/
@ -200,78 +215,107 @@ typedef NS_ENUM(NSUInteger, Signal) {
/**
* <p>Starts an asynchronous FFmpeg execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFmpegSessionCompleteCallback if you want to be
* notified about the result.
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param ffmpegSession FFmpeg session which includes command
* options/arguments
* @param queue dispatch queue that will be used to run this
* asynchronous operation
*/
+ (void)asyncFFmpegExecute:(FFmpegSession*)ffmpegSession onDispatchQueue:(dispatch_queue_t)queue;
+ (void)asyncFFmpegExecute:(FFmpegSession *)ffmpegSession
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
* @param ffprobeSession FFprobe session which includes command
* options/arguments
*/
+ (void)asyncFFprobeExecute:(FFprobeSession *)ffprobeSession;
/**
* <p>Starts an asynchronous FFprobe execution for the given session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an FFprobeSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an FFprobeSessionCompleteCallback if you want to be
* notified about the result.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param ffprobeSession FFprobe session which includes command
* options/arguments
* @param queue dispatch queue that will be used to run this
* asynchronous operation
*/
+ (void)asyncFFprobeExecute:(FFprobeSession*)ffprobeSession onDispatchQueue:(dispatch_queue_t)queue;
+ (void)asyncFFprobeExecute:(FFprobeSession *)ffprobeSession
onDispatchQueue:(dispatch_queue_t)queue;
/**
* <p>Starts an asynchronous FFprobe execution for the given media information session.
* <p>Starts an asynchronous FFprobe execution for the given media information
* session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an MediaInformationSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param waitTimeout max time to wait until media information is transmitted
* @param mediaInformationSession media information session which includes
* command options/arguments
* @param waitTimeout max time to wait until media information is
* transmitted
*/
+ (void)asyncGetMediaInformationExecute:(MediaInformationSession*)mediaInformationSession withTimeout:(int)waitTimeout;
+ (void)asyncGetMediaInformationExecute:
(MediaInformationSession *)mediaInformationSession
withTimeout:(int)waitTimeout;
/**
* <p>Starts an asynchronous FFprobe execution for the given media information session.
* <p>Starts an asynchronous FFprobe execution for the given media information
* session.
*
* <p>Note that this method returns immediately and does not wait the execution to complete.
* You must use an MediaInformationSessionCompleteCallback if you want to be notified about the result.
* <p>Note that this method returns immediately and does not wait the execution
* to complete. You must use an MediaInformationSessionCompleteCallback if you
* want to be notified about the result.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param queue dispatch queue that will be used to run this asynchronous operation
* @param waitTimeout max time to wait until media information is transmitted
* @param mediaInformationSession media information session which includes
* command options/arguments
* @param queue dispatch queue that will be used to run this
* asynchronous operation
* @param waitTimeout max time to wait until media information is
* transmitted
*/
+ (void)asyncGetMediaInformationExecute:(MediaInformationSession*)mediaInformationSession onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout;
+ (void)asyncGetMediaInformationExecute:
(MediaInformationSession *)mediaInformationSession
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout;
/**
* <p>Sets a global log callback to redirect FFmpeg/FFprobe logs.
*
* @param logCallback log callback or nil to disable a previously defined log callback
* @param logCallback log callback or nil to disable a previously defined log
* callback
*/
+ (void)enableLogCallback:(LogCallback)logCallback;
/**
* <p>Sets a global statistics callback to redirect FFmpeg statistics.
*
* @param statisticsCallback statistics callback or nil to disable a previously defined statistics callback
* @param statisticsCallback statistics callback or nil to disable a previously
* defined statistics callback
*/
+ (void)enableStatisticsCallback:(StatisticsCallback)statisticsCallback;
/**
* <p>Sets a global FFmpegSessionCompleteCallback to receive execution results for FFmpeg sessions.
* <p>Sets a global FFmpegSessionCompleteCallback to receive execution results
* for FFmpeg sessions.
*
* @param ffmpegSessionCompleteCallback complete callback or nil to disable a previously defined callback
* @param ffmpegSessionCompleteCallback complete callback or nil to disable a
* previously defined callback
*/
+ (void)enableFFmpegSessionCompleteCallback:(FFmpegSessionCompleteCallback)ffmpegSessionCompleteCallback;
+ (void)enableFFmpegSessionCompleteCallback:
(FFmpegSessionCompleteCallback)ffmpegSessionCompleteCallback;
/**
* <p>Returns the global FFmpegSessionCompleteCallback set.
@ -281,11 +325,14 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (FFmpegSessionCompleteCallback)getFFmpegSessionCompleteCallback;
/**
* <p>Sets a global FFprobeSessionCompleteCallback to receive execution results for FFprobe sessions.
* <p>Sets a global FFprobeSessionCompleteCallback to receive execution results
* for FFprobe sessions.
*
* @param ffprobeSessionCompleteCallback complete callback or nil to disable a previously defined callback
* @param ffprobeSessionCompleteCallback complete callback or nil to disable a
* previously defined callback
*/
+ (void)enableFFprobeSessionCompleteCallback:(FFprobeSessionCompleteCallback)ffprobeSessionCompleteCallback;
+ (void)enableFFprobeSessionCompleteCallback:
(FFprobeSessionCompleteCallback)ffprobeSessionCompleteCallback;
/**
* <p>Returns the global FFprobeSessionCompleteCallback set.
@ -295,19 +342,24 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (FFprobeSessionCompleteCallback)getFFprobeSessionCompleteCallback;
/**
* <p>Sets a global MediaInformationSessionCompleteCallback to receive execution results for MediaInformation sessions.
* <p>Sets a global MediaInformationSessionCompleteCallback to receive execution
* results for MediaInformation sessions.
*
* @param mediaInformationSessionCompleteCallback complete callback or nil to disable a previously defined
* callback
* @param mediaInformationSessionCompleteCallback complete callback or nil to
* disable a previously defined callback
*/
+ (void)enableMediaInformationSessionCompleteCallback:(MediaInformationSessionCompleteCallback)mediaInformationSessionCompleteCallback;
+ (void)enableMediaInformationSessionCompleteCallback:
(MediaInformationSessionCompleteCallback)
mediaInformationSessionCompleteCallback;
/**
* <p>Returns the global MediaInformationSessionCompleteCallback set.
*
* @return global MediaInformationSessionCompleteCallback or nil if it is not set
* @return global MediaInformationSessionCompleteCallback or nil if it is not
* set
*/
+ (MediaInformationSessionCompleteCallback)getMediaInformationSessionCompleteCallback;
+ (MediaInformationSessionCompleteCallback)
getMediaInformationSessionCompleteCallback;
/**
* Returns the current log level.
@ -346,13 +398,23 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (void)setSessionHistorySize:(int)sessionHistorySize;
/**
* Returns the session specified with <code>sessionId</code> from the session history.
* Returns the session specified with <code>sessionId</code> from the session
* history.
*
* @param sessionId session identifier
* @return session specified with sessionId or nil if it is not found in the history
* @return session specified with sessionId or nil if it is not found in the
* history
*/
+ (id<Session>)getSession:(long)sessionId;
/**
* Deletes the session specified with <code>sessionId</code> from the session
* history.
*
* @param sessionId session identifier
*/
+ (void)deleteSession:(long)sessionId;
/**
* Returns the last session created from the session history.
*
@ -363,8 +425,8 @@ typedef NS_ENUM(NSUInteger, Signal) {
/**
* Returns the last session completed from the session history.
*
* @return the last session completed. If there are no completed sessions in the history this
* method will return nil
* @return the last session completed. If there are no completed sessions in the
* history this method will return nil
*/
+ (id<Session>)getLastCompletedSession;
@ -421,14 +483,16 @@ typedef NS_ENUM(NSUInteger, Signal) {
*
* @param logRedirectionStrategy log redirection strategy
*/
+ (void)setLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy;
+ (void)setLogRedirectionStrategy:
(LogRedirectionStrategy)logRedirectionStrategy;
/**
* <p>Returns the number of async messages that are not transmitted to the callbacks for
* this session.
* <p>Returns the number of async messages that are not transmitted to the
* callbacks for this session.
*
* @param sessionId id of the session
* @return number of async messages that are not transmitted to the callbacks for this session
* @return number of async messages that are not transmitted to the callbacks
* for this session
*/
+ (int)messagesInTransmit:(long)sessionId;
@ -441,8 +505,8 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (NSString *)sessionStateToString:(SessionState)state;
/**
* <p>Parses the given command into arguments. Uses space character to split the arguments.
* Supports single and double quote characters.
* <p>Parses the given command into arguments. Uses space character to split the
* arguments. Supports single and double quote characters.
*
* @param command string command
* @return array of arguments
@ -450,7 +514,8 @@ typedef NS_ENUM(NSUInteger, Signal) {
+ (NSArray *)parseArguments:(NSString *)command;
/**
* <p>Concatenates arguments into a string adding a space character between two arguments.
* <p>Concatenates arguments into a string adding a space character between two
* arguments.
*
* @param arguments arguments
* @return concatenated string containing all arguments

File diff suppressed because it is too large Load Diff

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

@ -34,24 +34,54 @@
}
+ (instancetype)create:(NSArray *)arguments {
return [[self alloc] init:arguments withCompleteCallback:nil withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback withLogRedirectionStrategy:logRedirectionStrategy];
}
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
self = [super init:arguments withLogCallback:logCallback withLogRedirectionStrategy:logRedirectionStrategy];
return [[self alloc] init:arguments
withCompleteCallback:nil
withLogCallback:nil
withStatisticsCallback:nil
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:nil
withStatisticsCallback:nil
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback
withLogRedirectionStrategy:logRedirectionStrategy];
}
- (instancetype)init:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
self = [super init:arguments
withLogCallback:logCallback
withLogRedirectionStrategy:logRedirectionStrategy];
if (self) {
_statisticsCallback = statisticsCallback;
@ -75,14 +105,20 @@
[self waitForAsynchronousMessagesInTransmit:waitTimeout];
if ([self thereAreAsynchronousMessagesInTransmit]) {
NSLog(@"getAllStatisticsWithTimeout was called to return all statistics but there are still statistics being transmitted for session id %ld.", [self getSessionId]);
NSLog(
@"getAllStatisticsWithTimeout was called to return all statistics "
@"but "
@"there are still statistics being transmitted for session id %ld.",
[self getSessionId]);
}
return [self getStatistics];
}
- (NSArray *)getAllStatistics {
return [self getAllStatisticsWithTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return [self
getAllStatisticsWithTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
}
- (NSArray *)getStatistics {
@ -124,4 +160,3 @@
}
@end

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

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

@ -29,7 +29,10 @@
}
+ (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 {
@ -38,117 +41,236 @@
return session;
}
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback];
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback {
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback];
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback];
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session;
}
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback];
+ (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session;
}
+ (FFprobeSession *)execute:(NSString *)command {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command]];
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig ffprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback];
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback];
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session];
return session;
}
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback];
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session;
}
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback];
+ (FFprobeSession *)executeAsync:(NSString *)command
withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session;
}
+ (MediaInformationSession *)getMediaInformation:(NSString *)path {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments];
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments];
[FFmpegKitConfig
getMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session;
}
+ (MediaInformationSession*)getMediaInformation:(NSString*)path withTimeout:(int)waitTimeout {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments];
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:waitTimeout];
+ (MediaInformationSession *)getMediaInformation:(NSString *)path
withTimeout:(int)waitTimeout {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments];
[FFmpegKitConfig getMediaInformationExecute:session
withTimeout:waitTimeout];
return session;
}
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig
asyncGetMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session;
}
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withTimeout:(int)waitTimeout {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session withTimeout:waitTimeout];
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
withTimeout:(int)waitTimeout {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
withTimeout:waitTimeout];
return session;
}
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig
asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session;
}
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout];
+ (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session;
}
+ (MediaInformationSession*)getMediaInformationFromCommand:(NSString*)command {
MediaInformationSession* session = [MediaInformationSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
+ (MediaInformationSession *)getMediaInformationFromCommand:
(NSString *)command {
MediaInformationSession *session = [MediaInformationSession
create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig
getMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session;
}
+ (MediaInformationSession*)getMediaInformationFromCommandAsync:(NSString*)command withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout {
MediaInformationSession* session = [MediaInformationSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout];
+ (MediaInformationSession *)
getMediaInformationFromCommandAsync:(NSString *)command
withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
MediaInformationSession *session =
[MediaInformationSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session;
}
+ (MediaInformationSession*)getMediaInformationFromCommandArgumentsAsync:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout {
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout];
+ (MediaInformationSession *)
getMediaInformationFromCommandArgumentsAsync:(NSArray *)arguments
withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session;
}

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

@ -30,24 +30,47 @@
}
+ (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 {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:nil withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:nil
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withLogRedirectionStrategy:[FFmpegKitConfig getLogRedirectionStrategy]];
}
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withLogRedirectionStrategy:logRedirectionStrategy];
+ (instancetype)create:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withLogRedirectionStrategy:logRedirectionStrategy];
}
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
- (instancetype)init:(NSArray *)arguments
withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
self = [super init:arguments withLogCallback:logCallback withLogRedirectionStrategy:logRedirectionStrategy];
self = [super init:arguments
withLogCallback:logCallback
withLogRedirectionStrategy:logRedirectionStrategy];
if (self) {
_completeCallback = completeCallback;
@ -73,4 +96,3 @@
}
@end

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

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

@ -20,8 +20,8 @@
#ifndef FFMPEG_KIT_LOG_CALLBACK_H
#define FFMPEG_KIT_LOG_CALLBACK_H
#import <Foundation/Foundation.h>
#import "Log.h"
#import <Foundation/Foundation.h>
/**
* <p>Callback that receives logs generated for <code>FFmpegKit</code> sessions.

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

File diff suppressed because it is too large Load Diff

@ -20,9 +20,9 @@
#ifndef FFMPEG_KIT_MEDIA_INFORMATION_H
#define FFMPEG_KIT_MEDIA_INFORMATION_H
#import <Foundation/Foundation.h>
#import "Chapter.h"
#import "StreamInformation.h"
#import <Foundation/Foundation.h>
extern NSString *const MediaKeyMediaProperties;
extern NSString *const MediaKeyFilename;
@ -39,7 +39,9 @@ extern NSString* const MediaKeyTags;
*/
@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.
@ -156,7 +158,8 @@ extern NSString* const MediaKeyTags;
/**
* 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;

@ -45,10 +45,11 @@ NSString* const MediaKeyTags = @"tags";
* Stores chapters.
*/
NSArray *chapterArray;
}
- (instancetype)init:(NSDictionary*)mediaDictionary withStreams:(NSArray*)streams withChapters:(NSArray*)chapters{
- (instancetype)init:(NSDictionary *)mediaDictionary
withStreams:(NSArray *)streams
withChapters:(NSArray *)chapters {
self = [super init];
if (self) {
dictionary = mediaDictionary;

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

@ -28,37 +28,52 @@ NSString* const MediaInformationJsonParserKeyChapters = @"chapters";
@try {
return [self fromWithError:ffprobeJsonOutput];
} @catch (NSException *exception) {
NSLog(@"MediaInformation parsing failed: %@.\n", [NSString stringWithFormat:@"%@\n%@", [exception userInfo], [exception callStackSymbols]]);
NSLog(@"MediaInformation parsing failed: %@.\n",
[NSString stringWithFormat:@"%@\n%@", [exception userInfo],
[exception callStackSymbols]]);
return nil;
}
}
+ (MediaInformation *)fromWithError:(NSString *)ffprobeJsonOutput {
NSError *error = nil;
NSData *jsonData = [ffprobeJsonOutput dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
NSData *jsonData =
[ffprobeJsonOutput dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDictionary =
[NSJSONSerialization JSONObjectWithData:jsonData
options:kNilOptions
error:&error];
if (error != nil) {
@throw [NSException exceptionWithName:@"ParsingException" reason:[NSString stringWithFormat:@"%ld",(long)[error code]] userInfo:[error userInfo]];
@throw [NSException
exceptionWithName:@"ParsingException"
reason:[NSString
stringWithFormat:@"%ld", (long)[error code]]
userInfo:[error userInfo]];
}
if (jsonDictionary == nil) {
return nil;
}
NSArray* jsonStreamArray = [jsonDictionary objectForKey:MediaInformationJsonParserKeyStreams];
NSArray *jsonStreamArray =
[jsonDictionary objectForKey:MediaInformationJsonParserKeyStreams];
NSMutableArray *streamArray = [[NSMutableArray alloc] init];
for (int i = 0; i < jsonStreamArray.count; i++) {
NSDictionary *streamDictionary = [jsonStreamArray objectAtIndex:i];
[streamArray addObject:[[StreamInformation alloc] init:streamDictionary]];
[streamArray
addObject:[[StreamInformation alloc] init:streamDictionary]];
}
NSArray* jsonChapterArray = [jsonDictionary objectForKey:MediaInformationJsonParserKeyChapters];
NSArray *jsonChapterArray =
[jsonDictionary objectForKey:MediaInformationJsonParserKeyChapters];
NSMutableArray *chapterArray = [[NSMutableArray alloc] init];
for (int i = 0; i < jsonChapterArray.count; i++) {
NSDictionary *chapterDictionary = [jsonChapterArray objectAtIndex:i];
[chapterArray addObject:[[Chapter alloc] init:chapterDictionary]];
}
return [[MediaInformation alloc] init:jsonDictionary withStreams:streamArray withChapters:chapterArray];
return [[MediaInformation alloc] init:jsonDictionary
withStreams:streamArray
withChapters:chapterArray];
}
@end

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

Loading…
Cancel
Save