upgrade ffmpeg-kit api to 6.1.2

development
Taner Sener 9 months ago
parent a68a8c47a4
commit aa73d96c7c
  1. 2
      android/ffmpeg-kit-android-lib/Doxyfile
  2. 20
      android/ffmpeg-kit-android-lib/src/main/cpp/android_support.c
  3. 182
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpeg_context.c
  4. 145
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpeg_context.h
  5. 378
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.c
  6. 68
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit.h
  7. 42
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_abidetect.c
  8. 14
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_abidetect.h
  9. 2
      android/ffmpeg-kit-android-lib/src/main/cpp/ffmpegkit_exception.h
  10. 25
      android/ffmpeg-kit-android-lib/src/main/cpp/ffprobekit.c
  11. 5
      android/ffmpeg-kit-android-lib/src/main/cpp/ffprobekit.h
  12. 536
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.c
  13. 138
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_cmdutils.h
  14. 5422
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.c
  15. 485
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg.h
  16. 1168
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_dec.c
  17. 1367
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_demux.c
  18. 962
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_enc.c
  19. 3058
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_filter.c
  20. 320
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_hw.c
  21. 508
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.c
  22. 129
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux.h
  23. 2474
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_mux_init.c
  24. 837
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffmpeg_opt.c
  25. 3126
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_ffprobe.c
  26. 14
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_fopen_utf8.h
  27. 51
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.c
  28. 9
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_objpool.h
  29. 596
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.c
  30. 8
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_opt_common.h
  31. 448
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.c
  32. 38
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_sync_queue.h
  33. 30
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.c
  34. 8
      android/ffmpeg-kit-android-lib/src/main/cpp/fftools_thread_queue.h
  35. 4
      android/jni/Android.mk
  36. 2
      android/jni/ffmpeg/Android.mk
  37. 2
      android/jni/ffmpeg/neon/Android.mk
  38. 10
      apple/.gitignore
  39. 2
      apple/Doxyfile
  40. 1251
      apple/aclocal.m4
  41. 271
      apple/ar-lib
  42. 348
      apple/compile
  43. 1748
      apple/config.guess
  44. 1884
      apple/config.sub
  45. 4
      apple/configure.ac
  46. 791
      apple/depcomp
  47. 541
      apple/install-sh
  48. 215
      apple/missing
  49. 1
      apple/src/.gitignore
  50. 16
      apple/src/AbstractSession.h
  51. 99
      apple/src/AbstractSession.m
  52. 4
      apple/src/ArchDetect.h
  53. 33
      apple/src/ArchDetect.m
  54. 2
      apple/src/AtomicLong.m
  55. 45
      apple/src/Chapter.h
  56. 45
      apple/src/Chapter.m
  57. 180
      apple/src/FFmpegKit.h
  58. 99
      apple/src/FFmpegKit.m
  59. 281
      apple/src/FFmpegKitConfig.h
  60. 705
      apple/src/FFmpegKitConfig.m
  61. 56
      apple/src/FFmpegSession.h
  62. 97
      apple/src/FFmpegSession.m
  63. 15
      apple/src/FFmpegSessionCompleteCallback.h
  64. 347
      apple/src/FFprobeKit.h
  65. 240
      apple/src/FFprobeKit.m
  66. 16
      apple/src/FFprobeSession.h
  67. 44
      apple/src/FFprobeSession.m
  68. 21
      apple/src/FFprobeSessionCompleteCallback.h
  69. 5
      apple/src/Level.h
  70. 4
      apple/src/Log.h
  71. 4
      apple/src/Log.m
  72. 4
      apple/src/LogCallback.h
  73. 4
      apple/src/Makefile.am
  74. 1164
      apple/src/Makefile.in
  75. 71
      apple/src/MediaInformation.h
  76. 71
      apple/src/MediaInformation.m
  77. 10
      apple/src/MediaInformationJsonParser.h
  78. 45
      apple/src/MediaInformationJsonParser.m
  79. 25
      apple/src/MediaInformationSession.h
  80. 39
      apple/src/MediaInformationSession.m
  81. 23
      apple/src/MediaInformationSessionCompleteCallback.h
  82. 4
      apple/src/Packages.h
  83. 34
      apple/src/Packages.m
  84. 4
      apple/src/ReturnCode.h
  85. 6
      apple/src/ReturnCode.m
  86. 103
      apple/src/Session.h
  87. 9
      apple/src/Statistics.h
  88. 9
      apple/src/Statistics.m
  89. 7
      apple/src/StatisticsCallback.h
  90. 89
      apple/src/StreamInformation.h
  91. 89
      apple/src/StreamInformation.m
  92. 182
      apple/src/ffmpeg_context.c
  93. 145
      apple/src/ffmpeg_context.h
  94. 2
      apple/src/ffmpegkit_exception.h
  95. 536
      apple/src/fftools_cmdutils.c
  96. 138
      apple/src/fftools_cmdutils.h
  97. 5422
      apple/src/fftools_ffmpeg.c
  98. 485
      apple/src/fftools_ffmpeg.h
  99. 1168
      apple/src/fftools_ffmpeg_dec.c
  100. 1367
      apple/src/fftools_ffmpeg_demux.c
  101. Some files were not shown because too many files have changed in this diff Show More

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

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

@ -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,145 @@
/*
* 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
#if HAVE_TERMIOS_H
#include <termios.h>
#endif
#include "fftools_ffmpeg.h"
#include "libavformat/avio.h"
#include "libavutil/dict.h"
extern __thread BenchmarkTimeStamps current_time;
extern __thread struct termios oldtty;
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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
/* /*
* This file is part of FFmpeg. * This file is part of FFmpeg.
* Copyright (c) 2018-2022 Taner Sener * 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 * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -19,17 +19,22 @@
*/ */
/* /*
* This file is the modified version of ffmpeg.h file living in ffmpeg source code under the fftools folder. We * This file is the modified version of ffmpeg.h file living in ffmpeg source
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * code under the fftools folder. We manually update it each time we depend on a
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries. * new ffmpeg version. Below you can see the list of changes applied by us to
* develop mobile-ffmpeg and later ffmpeg-kit libraries.
* *
* ffmpeg-kit changes by ARTHENICA LTD * ffmpeg-kit changes by ARTHENICA LTD
* *
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
* - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT, SPECIFIER_OPT_FMT declarations migrated to * - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT,
* ffmpeg_mux.h * SPECIFIER_OPT_FMT declarations migrated to ffmpeg_mux.h
* - "class" member field renamed as clazz * - "class" member field renamed as clazz
* - time field in set_report_callback updated as double * - time field in set_report_callback updated as double
* *
@ -39,7 +44,8 @@
* -------------------------------------------------------- * --------------------------------------------------------
* - config.h include added back * - config.h include added back
* - volatile dropped from thread local variables * - 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 * 06.2020
* -------------------------------------------------------- * --------------------------------------------------------
@ -47,8 +53,9 @@
* *
* 12.2019 * 12.2019
* -------------------------------------------------------- * --------------------------------------------------------
* - concurrent execution support ("__thread" specifier added to variables used by multiple threads, * - concurrent execution support ("__thread" specifier added to variables used
* signatures of ffmpeg_opt.c methods called by both ffmpeg and ffprobe added) * by multiple threads, signatures of ffmpeg_opt.c methods called by both ffmpeg
* and ffprobe added)
* *
* 03.2019 * 03.2019
* -------------------------------------------------------- * --------------------------------------------------------
@ -70,10 +77,10 @@
#include "config.h" #include "config.h"
#include <signal.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h>
#include "fftools_cmdutils.h" #include "fftools_cmdutils.h"
#include "fftools_sync_queue.h" #include "fftools_sync_queue.h"
@ -103,6 +110,11 @@
#define FFMPEG_OPT_MAP_CHANNEL 1 #define FFMPEG_OPT_MAP_CHANNEL 1
#define FFMPEG_OPT_MAP_SYNC 1 #define FFMPEG_OPT_MAP_SYNC 1
#define FFMPEG_ROTATION_METADATA 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 { enum VideoSyncMethod {
VSYNC_AUTO = -1, VSYNC_AUTO = -1,
@ -113,7 +125,10 @@ enum VideoSyncMethod {
VSYNC_DROP, VSYNC_DROP,
}; };
#define MAX_STREAMS 1024 /* arbitrary sanity check value */ enum EncTimeBase {
ENC_TIME_BASE_DEMUX = -1,
ENC_TIME_BASE_FILTER = -2,
};
enum HWAccelID { enum HWAccelID {
HWACCEL_NONE = 0, HWACCEL_NONE = 0,
@ -142,6 +157,12 @@ typedef struct {
} AudioChannelMap; } AudioChannelMap;
#endif #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 { typedef struct OptionsContext {
OptionGroup *g; OptionGroup *g;
@ -173,6 +194,7 @@ typedef struct OptionsContext {
int loop; int loop;
int rate_emu; int rate_emu;
float readrate; float readrate;
double readrate_initial_burst;
int accurate_seek; int accurate_seek;
int thread_queue_size; int thread_queue_size;
int input_sync_ref; int input_sync_ref;
@ -217,9 +239,8 @@ typedef struct OptionsContext {
int subtitle_disable; int subtitle_disable;
int data_disable; int data_disable;
/* indexed by output file stream index */ // keys are stream indices
int *streamid_map; AVDictionary *streamid;
int nb_streamid_map;
SpecifierOpt *metadata; SpecifierOpt *metadata;
int nb_metadata; int nb_metadata;
@ -255,8 +276,10 @@ typedef struct OptionsContext {
int nb_inter_matrices; int nb_inter_matrices;
SpecifierOpt *chroma_intra_matrices; SpecifierOpt *chroma_intra_matrices;
int nb_chroma_intra_matrices; int nb_chroma_intra_matrices;
#if FFMPEG_OPT_TOP
SpecifierOpt *top_field_first; SpecifierOpt *top_field_first;
int nb_top_field_first; int nb_top_field_first;
#endif
SpecifierOpt *metadata_map; SpecifierOpt *metadata_map;
int nb_metadata_map; int nb_metadata_map;
SpecifierOpt *presets; SpecifierOpt *presets;
@ -318,62 +341,33 @@ typedef struct OptionsContext {
} OptionsContext; } OptionsContext;
typedef struct InputFilter { typedef struct InputFilter {
AVFilterContext *filter;
struct InputStream *ist;
struct FilterGraph *graph; struct FilterGraph *graph;
uint8_t *name; 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; } InputFilter;
typedef struct OutputFilter { typedef struct OutputFilter {
AVFilterContext *filter;
struct OutputStream *ost; struct OutputStream *ost;
struct FilterGraph *graph; struct FilterGraph *graph;
uint8_t *name; uint8_t *name;
/* temporary storage until stream maps are processed */ /* for filters that are not yet bound to an output stream,
AVFilterInOut *out_tmp; * this stores the output linklabel, if any */
uint8_t *linklabel;
enum AVMediaType type; enum AVMediaType type;
/* desired output stream properties */ /* pts of the last frame received from this filter, in AV_TIME_BASE_Q */
int width, height; int64_t last_pts;
AVRational frame_rate;
int format; uint64_t nb_frames_dup;
int sample_rate; uint64_t nb_frames_drop;
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;
} OutputFilter; } OutputFilter;
typedef struct FilterGraph { typedef struct FilterGraph {
const AVClass *clazz;
int index; int index;
const char *graph_desc;
AVFilterGraph *graph; AVFilterGraph *graph;
int reconfiguration;
// true when the filtergraph contains only meta filters
// that do not modify the frame data
int is_meta;
InputFilter **inputs; InputFilter **inputs;
int nb_inputs; int nb_inputs;
@ -381,17 +375,21 @@ typedef struct FilterGraph {
int nb_outputs; int nb_outputs;
} FilterGraph; } FilterGraph;
typedef struct Decoder Decoder;
typedef struct InputStream { typedef struct InputStream {
const AVClass *clazz;
int file_index; int file_index;
int index;
AVStream *st; AVStream *st;
int discard; /* true if stream data should be discarded */ int discard; /* true if stream data should be discarded */
int user_set_discard; 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_OST 1
#define DECODING_FOR_FILTER 2 #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. * Codec parameters - to be used by the decoding/streamcopy code.
@ -399,65 +397,28 @@ typedef struct InputStream {
* concurrently by the demuxing thread. * concurrently by the demuxing thread.
*/ */
AVCodecParameters *par; AVCodecParameters *par;
Decoder *decoder;
AVCodecContext *dec_ctx; AVCodecContext *dec_ctx;
const AVCodec *dec; const AVCodec *dec;
AVFrame *decoded_frame; const AVCodecDescriptor *codec_desc;
AVPacket *pkt;
AVRational framerate_guessed; AVRational framerate_guessed;
int64_t prev_pkt_pts; int64_t nb_samples; /* number of samples in the last decoded audio frame
int64_t start; /* time when read started */ before looping */
/* 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 */
double ts_scale;
int saw_first_ts;
AVDictionary *decoder_opts; AVDictionary *decoder_opts;
AVRational framerate; /* framerate forced with -r */ AVRational framerate; /* framerate forced with -r */
#if FFMPEG_OPT_TOP
int top_field_first; int top_field_first;
int guess_layout_max; #endif
int autorotate; int autorotate;
int fix_sub_duration; int fix_sub_duration;
struct { /* previous decoded subtitle and related variables */
int got_output;
int ret;
AVSubtitle subtitle;
} prev_sub;
struct sub2video { struct sub2video {
int64_t last_pts;
int64_t end_pts;
AVFifo *sub_queue; ///< queue of AVSubtitle* before filter init
AVFrame *frame;
int w, h; int w, h;
unsigned int initialize; ///< marks if sub2video_update should force an initialization
} sub2video; } sub2video;
/* decoded data from this stream goes into all those filters /* decoded data from this stream goes into all those filters
@ -465,6 +426,14 @@ typedef struct InputStream {
InputFilter **filters; InputFilter **filters;
int nb_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; int reinit_filters;
/* hwaccel options */ /* hwaccel options */
@ -473,22 +442,11 @@ typedef struct InputStream {
char *hwaccel_device; char *hwaccel_device;
enum AVPixelFormat hwaccel_output_format; enum AVPixelFormat hwaccel_output_format;
int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame);
enum AVPixelFormat hwaccel_pix_fmt;
/* stats */ /* 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 // number of frames/samples retrieved from the decoder
uint64_t frames_decoded; uint64_t frames_decoded;
uint64_t samples_decoded; uint64_t samples_decoded;
uint64_t decode_errors;
int64_t *dts_buffer;
int nb_dts_buffer;
int got_output;
} InputStream; } InputStream;
typedef struct LastFrameDuration { typedef struct LastFrameDuration {
@ -497,8 +455,13 @@ typedef struct LastFrameDuration {
} LastFrameDuration; } LastFrameDuration;
typedef struct InputFile { typedef struct InputFile {
const AVClass *clazz;
int index; int index;
// input format has no timestamps
int format_nots;
AVFormatContext *ctx; AVFormatContext *ctx;
int eof_reached; /* true if eof reached */ int eof_reached; /* true if eof reached */
int eagain; /* true if last read attempt returned EAGAIN */ int eagain; /* true if last read attempt returned EAGAIN */
@ -509,12 +472,8 @@ typedef struct InputFile {
*/ */
int64_t start_time_effective; int64_t start_time_effective;
int64_t ts_offset; int64_t ts_offset;
/** int64_t start_time; /* user-specified start time in AV_TIME_BASE or
* Extra timestamp offset added by discontinuity handling. AV_NOPTS_VALUE */
*/
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 recording_time; int64_t recording_time;
/* streams that ffmpeg is aware of; /* streams that ffmpeg is aware of;
@ -523,7 +482,6 @@ typedef struct InputFile {
InputStream **streams; InputStream **streams;
int nb_streams; int nb_streams;
int rate_emu;
float readrate; float readrate;
int accurate_seek; int accurate_seek;
@ -585,11 +543,13 @@ extern const char *const forced_keyframes_const_names[];
typedef enum { typedef enum {
ENCODER_FINISHED = 1, ENCODER_FINISHED = 1,
MUXER_FINISHED = 2, MUXER_FINISHED = 2,
} OSTFinished ; } OSTFinished;
enum { enum {
KF_FORCE_SOURCE = 1, KF_FORCE_SOURCE = 1,
#if FFMPEG_OPT_FORCE_KF_SOURCE_NO_DROP
KF_FORCE_SOURCE_NO_DROP = 2, KF_FORCE_SOURCE_NO_DROP = 2,
#endif
}; };
typedef struct KeyframeForceCtx { typedef struct KeyframeForceCtx {
@ -608,44 +568,35 @@ typedef struct KeyframeForceCtx {
int dropped_keyframe; int dropped_keyframe;
} KeyframeForceCtx; } KeyframeForceCtx;
typedef struct Encoder Encoder;
typedef struct OutputStream { typedef struct OutputStream {
const AVClass *clazz; const AVClass *clazz;
enum AVMediaType type;
int file_index; /* file index */ int file_index; /* file index */
int index; /* stream index in the output file */ 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; /* input stream that is the source for this output stream;
* may be NULL for streams with no well-defined source, e.g. * may be NULL for streams with no well-defined source, e.g.
* attachments or outputs from complex filtergraphs */ * attachments or outputs from complex filtergraphs */
InputStream *ist; InputStream *ist;
AVStream *st; /* stream in the output file */ 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 */ /* dts of the last packet sent to the muxing queue, in AV_TIME_BASE_Q */
int64_t last_mux_dts; 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; AVRational enc_timebase;
Encoder *enc;
AVCodecContext *enc_ctx; 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 */ /* video only */
AVRational frame_rate; AVRational frame_rate;
@ -653,7 +604,9 @@ typedef struct OutputStream {
enum VideoSyncMethod vsync_method; enum VideoSyncMethod vsync_method;
int is_cfr; int is_cfr;
int force_fps; int force_fps;
#if FFMPEG_OPT_TOP
int top_field_first; int top_field_first;
#endif
#if FFMPEG_ROTATION_METADATA #if FFMPEG_ROTATION_METADATA
int rotate_overridden; int rotate_overridden;
#endif #endif
@ -670,7 +623,8 @@ typedef struct OutputStream {
/* audio only */ /* audio only */
#if FFMPEG_OPT_MAP_CHANNEL #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 */ int audio_channels_mapped; /* number of channels in audio_channels_map */
#endif #endif
@ -678,53 +632,35 @@ typedef struct OutputStream {
FILE *logfile; FILE *logfile;
OutputFilter *filter; 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 *encoder_opts;
AVDictionary *sws_dict; AVDictionary *sws_dict;
AVDictionary *swr_opts; AVDictionary *swr_opts;
char *apad; char *apad;
OSTFinished finished; /* no more packets should be written for this stream */ OSTFinished
int unavailable; /* true if the steram is unavailable (possibly temporarily) */ 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 // init_output_stream() has been called for this stream
// The encoder and the bitstream filters have been initialized and the stream // The encoder and the bitstream filters have been initialized and the
// parameters are set in the AVStream. // stream parameters are set in the AVStream.
int initialized; int initialized;
int inputs_done;
const char *attachment_filename; const char *attachment_filename;
int streamcopy_started;
int copy_initial_nonkeyframes;
int copy_prior_start;
int keep_pix_fmt; int keep_pix_fmt;
/* stats */ /* 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 // number of packets send to the muxer
atomic_uint_least64_t packets_written; atomic_uint_least64_t packets_written;
// number of frames/samples sent to the encoder // number of frames/samples sent to the encoder
uint64_t frames_encoded; uint64_t frames_encoded;
uint64_t samples_encoded; uint64_t samples_encoded;
// number of packets received from the encoder
uint64_t packets_encoded;
/* packet quality factor */ /* packet quality factor */
int quality; 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_encode;
int sq_idx_mux; int sq_idx_mux;
@ -751,13 +687,29 @@ typedef struct OutputFile {
SyncQueue *sq_encode; 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 int64_t start_time; ///< start time in microseconds == AV_TIME_BASE units
int shortest; int shortest;
int bitexact; int bitexact;
} OutputFile; } 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 InputFile **input_files;
extern __thread int nb_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 *vstats_filename;
extern __thread char *sdp_filename; extern __thread char *sdp_filename;
extern __thread float audio_drift_threshold;
extern __thread float dts_delta_threshold; extern __thread float dts_delta_threshold;
extern __thread float dts_error_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 abort_on_flags;
extern __thread int print_stats; extern __thread int print_stats;
extern __thread int64_t stats_period; extern __thread int64_t stats_period;
extern __thread int qp_hist;
extern __thread int stdin_interaction; extern __thread int stdin_interaction;
extern __thread AVIOContext *progress_avio; extern __thread AVIOContext *progress_avio;
extern __thread float max_error_rate; extern __thread float max_error_rate;
@ -810,6 +760,8 @@ extern __thread int copy_unknown_streams;
extern __thread int recast_media; extern __thread int recast_media;
extern __thread FILE *vstats_file;
#if FFMPEG_OPT_PSNR #if FFMPEG_OPT_PSNR
extern __thread int do_psnr; extern __thread int do_psnr;
#endif #endif
@ -820,40 +772,120 @@ void term_exit(void);
void show_usage(void); void show_usage(void);
void remove_avoptions(AVDictionary **a, AVDictionary *b); 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); char *file_read(const char *filename);
AVDictionary *strip_specifiers(const AVDictionary *dict); AVDictionary *strip_specifiers(const AVDictionary *dict);
const AVCodec *find_codec_or_die(void *logctx, const char *name, int find_codec(void *logctx, const char *name, enum AVMediaType type,
enum AVMediaType type, int encoder); 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 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 check_filter_outputs(void);
int filtergraph_is_simple(FilterGraph *fg); int filtergraph_is_simple(const FilterGraph *fg);
int init_simple_filtergraph(InputStream *ist, OutputStream *ost); int init_simple_filtergraph(InputStream *ist, OutputStream *ost,
char *graph_desc);
int init_complex_filtergraph(FilterGraph *fg); 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);
int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame); /**
* Get our axiliary frame data attached to the frame, allocating it
* if needed.
*/
FrameData *frame_data(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); int ffmpeg_parse_options(int argc, char **argv);
void enc_stats_write(OutputStream *ost, EncStats *es, void enc_stats_write(OutputStream *ost, EncStats *es, const AVFrame *frame,
const AVFrame *frame, const AVPacket *pkt, const AVPacket *pkt, uint64_t frame_num);
uint64_t frame_num);
HWDevice *hw_device_get_by_name(const char *name); 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_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); void hw_device_free_all(void);
int hw_device_setup_for_decode(InputStream *ist); /**
int hw_device_setup_for_encode(OutputStream *ost); * Get a hardware device to be used with this filtergraph.
int hw_device_setup_for_filter(FilterGraph *fg); * 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_decode_init(AVCodecContext *avctx); int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input);
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 * 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_stream_init(OutputFile *of, OutputStream *ost);
int of_write_trailer(OutputFile *of); int of_write_trailer(OutputFile *of);
int of_open(const OptionsContext *o, const char *filename); 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); void of_enc_stats_close(void);
/* int of_output_packet(OutputFile *of, OutputStream *ost, AVPacket *pkt);
* 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 * @param dts predicted packet dts in AV_TIME_BASE_Q
* 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.
*/ */
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); int64_t of_filesize(OutputFile *of);
int ifile_open(const OptionsContext *o, const char *filename); 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 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; /* iterate over all input streams in all input files;
* pass NULL to start iteration */ * pass NULL to start iteration */
InputStream *ist_iter(InputStream *prev); InputStream *ist_iter(InputStream *prev);
extern const char * const opt_name_codec_names[]; /* iterate over all output streams in all output files;
extern const char * const opt_name_codec_tags[]; * pass NULL to start iteration */
extern const char * const opt_name_frame_rates[]; OutputStream *ost_iter(OutputStream *prev);
extern const char * const opt_name_top_field_first[];
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); void cancel_operation(long id);
#endif /* FFTOOLS_FFMPEG_H */ #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) 2018-2019 Taner Sener
* Copyright (c) 2023 ARTHENICA LTD * Copyright (c) 2023-2024 ARTHENICA LTD
* *
* This file is part of FFmpeg. * This file is part of FFmpeg.
* *
@ -20,12 +20,17 @@
*/ */
/* /*
* This file is the modified version of ffmpeg_hw.c file living in ffmpeg source code under the fftools folder. We * This file is the modified version of ffmpeg_hw.c file living in ffmpeg source
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * code under the fftools folder. We manually update it each time we depend on a
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries. * new ffmpeg version. Below you can see the list of changes applied by us to
* develop mobile-ffmpeg and later ffmpeg-kit libraries.
* *
* ffmpeg-kit changes by ARTHENICA LTD * ffmpeg-kit changes by ARTHENICA LTD
* *
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
@ -34,7 +39,8 @@
* *
* 12.2019 * 12.2019
* -------------------------------------------------------- * --------------------------------------------------------
* - concurrent execution support ("__thread" specifier added to variables used by multiple threads) * - concurrent execution support ("__thread" specifier added to variables used
* by multiple threads)
* *
* 08.2018 * 08.2018
* -------------------------------------------------------- * --------------------------------------------------------
@ -43,17 +49,16 @@
#include <string.h> #include <string.h>
#include "libavfilter/buffersink.h"
#include "libavutil/avstring.h" #include "libavutil/avstring.h"
#include "libavutil/pixdesc.h" #include "libavutil/pixdesc.h"
#include "libavfilter/buffersink.h"
#include "fftools_ffmpeg.h" #include "fftools_ffmpeg.h"
__thread int nb_hw_devices; __thread int nb_hw_devices;
__thread HWDevice **hw_devices; __thread HWDevice **hw_devices;
static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) {
{
HWDevice *found = NULL; HWDevice *found = NULL;
int i; int i;
for (i = 0; i < nb_hw_devices; i++) { for (i = 0; i < nb_hw_devices; i++) {
@ -66,8 +71,7 @@ static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type)
return found; return found;
} }
HWDevice *hw_device_get_by_name(const char *name) HWDevice *hw_device_get_by_name(const char *name) {
{
int i; int i;
for (i = 0; i < nb_hw_devices; i++) { for (i = 0; i < nb_hw_devices; i++) {
if (!strcmp(hw_devices[i]->name, name)) if (!strcmp(hw_devices[i]->name, name))
@ -76,11 +80,10 @@ HWDevice *hw_device_get_by_name(const char *name)
return NULL; return NULL;
} }
static HWDevice *hw_device_add(void) static HWDevice *hw_device_add(void) {
{
int err; int err;
err = av_reallocp_array(&hw_devices, nb_hw_devices + 1, err =
sizeof(*hw_devices)); av_reallocp_array(&hw_devices, nb_hw_devices + 1, sizeof(*hw_devices));
if (err) { if (err) {
nb_hw_devices = 0; nb_hw_devices = 0;
return NULL; return NULL;
@ -91,8 +94,7 @@ static HWDevice *hw_device_add(void)
return hw_devices[nb_hw_devices++]; return hw_devices[nb_hw_devices++];
} }
static char *hw_device_default_name(enum AVHWDeviceType type) static char *hw_device_default_name(enum AVHWDeviceType type) {
{
// Make an automatic name of the form "type%d". We arbitrarily // Make an automatic name of the form "type%d". We arbitrarily
// limit at 1000 anonymous devices of the same type - there is // limit at 1000 anonymous devices of the same type - there is
// probably something else very wrong if you get to this limit. // probably something else very wrong if you get to this limit.
@ -116,8 +118,7 @@ static char *hw_device_default_name(enum AVHWDeviceType type)
return name; return name;
} }
int hw_device_init_from_string(const char *arg, HWDevice **dev_out) int hw_device_init_from_string(const char *arg, HWDevice **dev_out) {
{
// "type=name" // "type=name"
// "type=name,key=value,key2=value2" // "type=name,key=value,key2=value2"
// "type=name:device,key=value,key2=value2" // "type=name:device,key=value,key2=value2"
@ -174,8 +175,7 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
if (!*p) { if (!*p) {
// New device with no parameters. // New device with no parameters.
err = av_hwdevice_ctx_create(&device_ref, type, err = av_hwdevice_ctx_create(&device_ref, type, NULL, NULL, 0);
NULL, NULL, 0);
if (err < 0) if (err < 0)
goto fail; goto fail;
@ -199,7 +199,9 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
} }
err = av_hwdevice_ctx_create(&device_ref, type, err = av_hwdevice_ctx_create(&device_ref, type,
q ? device : p[0] ? p : NULL, q ? device
: p[0] ? p
: NULL,
options, 0); options, 0);
if (err < 0) if (err < 0)
goto fail; goto fail;
@ -213,8 +215,8 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
goto invalid; goto invalid;
} }
err = av_hwdevice_ctx_create_derived(&device_ref, type, err = av_hwdevice_ctx_create_derived(&device_ref, type, src->device_ref,
src->device_ref, 0); 0);
if (err < 0) if (err < 0)
goto fail; goto fail;
} else if (*p == ',') { } else if (*p == ',') {
@ -225,8 +227,7 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out)
goto invalid; goto invalid;
} }
err = av_hwdevice_ctx_create(&device_ref, type, err = av_hwdevice_ctx_create(&device_ref, type, NULL, options, 0);
NULL, options, 0);
if (err < 0) if (err < 0)
goto fail; goto fail;
} else { } else {
@ -256,21 +257,18 @@ done:
av_dict_free(&options); av_dict_free(&options);
return err; return err;
invalid: invalid:
av_log(NULL, AV_LOG_ERROR, av_log(NULL, AV_LOG_ERROR, "Invalid device specification \"%s\": %s\n", arg,
"Invalid device specification \"%s\": %s\n", arg, errmsg); errmsg);
err = AVERROR(EINVAL); err = AVERROR(EINVAL);
goto done; goto done;
fail: fail:
av_log(NULL, AV_LOG_ERROR, av_log(NULL, AV_LOG_ERROR, "Device creation failed: %d.\n", err);
"Device creation failed: %d.\n", err);
av_buffer_unref(&device_ref); av_buffer_unref(&device_ref);
goto done; goto done;
} }
static int hw_device_init_from_type(enum AVHWDeviceType type, int hw_device_init_from_type(enum AVHWDeviceType type, const char *device,
const char *device, HWDevice **dev_out) {
HWDevice **dev_out)
{
AVBufferRef *device_ref = NULL; AVBufferRef *device_ref = NULL;
HWDevice *dev; HWDevice *dev;
char *name; char *name;
@ -284,8 +282,7 @@ static int hw_device_init_from_type(enum AVHWDeviceType type,
err = av_hwdevice_ctx_create(&device_ref, type, device, NULL, 0); err = av_hwdevice_ctx_create(&device_ref, type, device, NULL, 0);
if (err < 0) { if (err < 0) {
av_log(NULL, AV_LOG_ERROR, av_log(NULL, AV_LOG_ERROR, "Device creation failed: %d.\n", err);
"Device creation failed: %d.\n", err);
goto fail; goto fail;
} }
@ -310,8 +307,7 @@ fail:
return err; return err;
} }
void hw_device_free_all(void) void hw_device_free_all(void) {
{
int i; int i;
for (i = 0; i < nb_hw_devices; i++) { for (i = 0; i < nb_hw_devices; i++) {
av_freep(&hw_devices[i]->name); av_freep(&hw_devices[i]->name);
@ -322,208 +318,7 @@ void hw_device_free_all(void)
nb_hw_devices = 0; nb_hw_devices = 0;
} }
static HWDevice *hw_device_match_by_codec(const AVCodec *codec) int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) {
{
const AVCodecHWConfig *config;
HWDevice *dev;
int i;
for (i = 0;; i++) {
config = avcodec_get_hw_config(codec, i);
if (!config)
return NULL;
if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
continue;
dev = hw_device_get_by_type(config->device_type);
if (dev)
return dev;
}
}
int hw_device_setup_for_decode(InputStream *ist)
{
const AVCodecHWConfig *config;
enum AVHWDeviceType type;
HWDevice *dev = NULL;
int err, auto_device = 0;
if (ist->hwaccel_device) {
dev = hw_device_get_by_name(ist->hwaccel_device);
if (!dev) {
if (ist->hwaccel_id == HWACCEL_AUTO) {
auto_device = 1;
} else if (ist->hwaccel_id == HWACCEL_GENERIC) {
type = ist->hwaccel_device_type;
err = hw_device_init_from_type(type, ist->hwaccel_device,
&dev);
} else {
// This will be dealt with by API-specific initialisation
// (using hwaccel_device), so nothing further needed here.
return 0;
}
} else {
if (ist->hwaccel_id == HWACCEL_AUTO) {
ist->hwaccel_device_type = dev->type;
} else if (ist->hwaccel_device_type != dev->type) {
av_log(NULL, AV_LOG_ERROR, "Invalid hwaccel device "
"specified for decoder: device %s of type %s is not "
"usable with hwaccel %s.\n", dev->name,
av_hwdevice_get_type_name(dev->type),
av_hwdevice_get_type_name(ist->hwaccel_device_type));
return AVERROR(EINVAL);
}
}
} else {
if (ist->hwaccel_id == HWACCEL_AUTO) {
auto_device = 1;
} else if (ist->hwaccel_id == HWACCEL_GENERIC) {
type = ist->hwaccel_device_type;
dev = hw_device_get_by_type(type);
// When "-qsv_device device" is used, an internal QSV device named
// as "__qsv_device" is created. Another QSV device is created too
// if "-init_hw_device qsv=name:device" is used. There are 2 QSV devices
// if both "-qsv_device device" and "-init_hw_device qsv=name:device"
// are used, hw_device_get_by_type(AV_HWDEVICE_TYPE_QSV) returns NULL.
// To keep back-compatibility with the removed ad-hoc libmfx setup code,
// call hw_device_get_by_name("__qsv_device") to select the internal QSV
// device.
if (!dev && type == AV_HWDEVICE_TYPE_QSV)
dev = hw_device_get_by_name("__qsv_device");
if (!dev)
err = hw_device_init_from_type(type, NULL, &dev);
} else {
dev = hw_device_match_by_codec(ist->dec);
if (!dev) {
// No device for this codec, but not using generic hwaccel
// and therefore may well not need one - ignore.
return 0;
}
}
}
if (auto_device) {
int i;
if (!avcodec_get_hw_config(ist->dec, 0)) {
// Decoder does not support any hardware devices.
return 0;
}
for (i = 0; !dev; i++) {
config = avcodec_get_hw_config(ist->dec, i);
if (!config)
break;
type = config->device_type;
dev = hw_device_get_by_type(type);
if (dev) {
av_log(NULL, AV_LOG_INFO, "Using auto "
"hwaccel type %s with existing device %s.\n",
av_hwdevice_get_type_name(type), dev->name);
}
}
for (i = 0; !dev; i++) {
config = avcodec_get_hw_config(ist->dec, i);
if (!config)
break;
type = config->device_type;
// Try to make a new device of this type.
err = hw_device_init_from_type(type, ist->hwaccel_device,
&dev);
if (err < 0) {
// Can't make a device of this type.
continue;
}
if (ist->hwaccel_device) {
av_log(NULL, AV_LOG_INFO, "Using auto "
"hwaccel type %s with new device created "
"from %s.\n", av_hwdevice_get_type_name(type),
ist->hwaccel_device);
} else {
av_log(NULL, AV_LOG_INFO, "Using auto "
"hwaccel type %s with new default device.\n",
av_hwdevice_get_type_name(type));
}
}
if (dev) {
ist->hwaccel_device_type = type;
} else {
av_log(NULL, AV_LOG_INFO, "Auto hwaccel "
"disabled: no device found.\n");
ist->hwaccel_id = HWACCEL_NONE;
return 0;
}
}
if (!dev) {
av_log(NULL, AV_LOG_ERROR, "No device available "
"for decoder: device type %s needed for codec %s.\n",
av_hwdevice_get_type_name(type), ist->dec->name);
return err;
}
ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
if (!ist->dec_ctx->hw_device_ctx)
return AVERROR(ENOMEM);
return 0;
}
int hw_device_setup_for_encode(OutputStream *ost)
{
const AVCodecHWConfig *config;
HWDevice *dev = NULL;
AVBufferRef *frames_ref = NULL;
int i;
if (ost->filter) {
frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
if (frames_ref &&
((AVHWFramesContext*)frames_ref->data)->format ==
ost->enc_ctx->pix_fmt) {
// Matching format, will try to use hw_frames_ctx.
} else {
frames_ref = NULL;
}
}
for (i = 0;; i++) {
config = avcodec_get_hw_config(ost->enc_ctx->codec, i);
if (!config)
break;
if (frames_ref &&
config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX &&
(config->pix_fmt == AV_PIX_FMT_NONE ||
config->pix_fmt == ost->enc_ctx->pix_fmt)) {
av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input "
"frames context (format %s) with %s encoder.\n",
av_get_pix_fmt_name(ost->enc_ctx->pix_fmt),
ost->enc_ctx->codec->name);
ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref);
if (!ost->enc_ctx->hw_frames_ctx)
return AVERROR(ENOMEM);
return 0;
}
if (!dev &&
config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
dev = hw_device_get_by_type(config->device_type);
}
if (dev) {
av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using device %s "
"(type %s) with %s encoder.\n", dev->name,
av_hwdevice_get_type_name(dev->type), ost->enc_ctx->codec->name);
ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
if (!ost->enc_ctx->hw_device_ctx)
return AVERROR(ENOMEM);
} else {
// No device required, or no device available.
}
return 0;
}
static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input)
{
InputStream *ist = avctx->opaque; InputStream *ist = avctx->opaque;
AVFrame *output = NULL; AVFrame *output = NULL;
enum AVPixelFormat output_format = ist->hwaccel_output_format; enum AVPixelFormat output_format = ist->hwaccel_output_format;
@ -542,8 +337,10 @@ static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input)
err = av_hwframe_transfer_data(output, input, 0); err = av_hwframe_transfer_data(output, input, 0);
if (err < 0) { if (err < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to transfer data to " av_log(avctx, AV_LOG_ERROR,
"output frame: %d.\n", err); "Failed to transfer data to "
"output frame: %d.\n",
err);
goto fail; goto fail;
} }
@ -564,45 +361,26 @@ fail:
return err; return err;
} }
int hwaccel_decode_init(AVCodecContext *avctx) AVBufferRef *hw_device_for_filter(void) {
{
InputStream *ist = avctx->opaque;
ist->hwaccel_retrieve_data = &hwaccel_retrieve_data;
return 0;
}
int hw_device_setup_for_filter(FilterGraph *fg)
{
HWDevice *dev;
int i;
// Pick the last hardware device if the user doesn't pick the device for // Pick the last hardware device if the user doesn't pick the device for
// filters explicitly with the filter_hw_device option. // filters explicitly with the filter_hw_device option.
if (filter_hw_device) if (filter_hw_device)
dev = filter_hw_device; return filter_hw_device->device_ref;
else if (nb_hw_devices > 0) { else if (nb_hw_devices > 0) {
dev = hw_devices[nb_hw_devices - 1]; HWDevice *dev = hw_devices[nb_hw_devices - 1];
if (nb_hw_devices > 1) if (nb_hw_devices > 1)
av_log(NULL, AV_LOG_WARNING, "There are %d hardware devices. device " av_log(
NULL, AV_LOG_WARNING,
"There are %d hardware devices. device "
"%s of type %s is picked for filters by default. Set hardware " "%s of type %s is picked for filters by default. Set hardware "
"device explicitly with the filter_hw_device option if device " "device explicitly with the filter_hw_device option if device "
"%s is not usable for filters.\n", "%s is not usable for filters.\n",
nb_hw_devices, dev->name, nb_hw_devices, dev->name, av_hwdevice_get_type_name(dev->type),
av_hwdevice_get_type_name(dev->type), dev->name); dev->name);
} else
dev = NULL; return dev->device_ref;
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);
}
} }
return 0; return NULL;
} }

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

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

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

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

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

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

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

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

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

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

@ -29,7 +29,7 @@ LOCAL_PATH := $(MY_LOCAL_PATH)/../ffmpeg-kit-android-lib/src/main/cpp
# DEFINE ARCH FLAGS # DEFINE ARCH FLAGS
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a) ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
MY_ARCH_FLAGS := ARM_V7A MY_ARCH_FLAGS := ARM_V7A
MY_ARM_NEON := false MY_ARM_NEON := true
endif endif
ifeq ($(TARGET_ARCH_ABI), arm64-v8a) ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
MY_ARCH_FLAGS := ARM64_V8A MY_ARCH_FLAGS := ARM64_V8A
@ -57,7 +57,7 @@ include $(BUILD_SHARED_LIBRARY)
$(call import-module, cpu-features) $(call import-module, cpu-features)
MY_SRC_FILES := ffmpegkit.c ffprobekit.c ffmpegkit_exception.c fftools_cmdutils.c fftools_ffmpeg.c fftools_ffprobe.c fftools_ffmpeg_mux.c fftools_ffmpeg_mux_init.c fftools_ffmpeg_demux.c fftools_ffmpeg_enc.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 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
MY_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare MY_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare
MY_LDLIBS := -llog -lz -landroid MY_LDLIBS := -llog -lz -landroid

@ -1,4 +1,4 @@
LOCAL_PATH := $(call my-dir)/../../../prebuilt/$(MY_BUILD_DIR)/ffmpeg/lib LOCAL_PATH := $(call my-dir)/../../../prebuilt/android-$(TARGET_ARCH)-$(API)$(MY_LTS_POSTFIX)/ffmpeg/lib
MY_ARM_MODE := arm MY_ARM_MODE := arm

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

10
apple/.gitignore vendored

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

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

1251
apple/aclocal.m4 vendored

File diff suppressed because it is too large Load Diff

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

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

1748
apple/config.guess vendored

File diff suppressed because it is too large Load Diff

1884
apple/config.sub vendored

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

@ -32,14 +32,14 @@
* *
* @return architecture name of the cpu running * @return architecture name of the cpu running
*/ */
+ (NSString*)getCpuArch; + (NSString *)getCpuArch;
/** /**
* Returns architecture name loaded. * Returns architecture name loaded.
* *
* @return architecture name loaded * @return architecture name loaded
*/ */
+ (NSString*)getArch; + (NSString *)getArch;
@end @end

@ -17,13 +17,13 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>. * along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/ */
#import <sys/types.h>
#import <sys/sysctl.h>
#import <mach/machine.h>
#import "ArchDetect.h" #import "ArchDetect.h"
#import "FFmpegKitConfig.h"
#import "FFmpegKit.h" #import "FFmpegKit.h"
#import "FFmpegKitConfig.h"
#import "FFprobeKit.h" #import "FFprobeKit.h"
#import <mach/machine.h>
#import <sys/sysctl.h>
#import <sys/types.h>
@implementation ArchDetect @implementation ArchDetect
@ -33,8 +33,8 @@
[FFprobeKit class]; [FFprobeKit class];
} }
+ (NSString*)getCpuArch { + (NSString *)getCpuArch {
NSMutableString* cpu = [[NSMutableString alloc] init]; NSMutableString *cpu = [[NSMutableString alloc] init];
size_t size; size_t size;
cpu_type_t type; cpu_type_t type;
cpu_subtype_t subtype; cpu_subtype_t subtype;
@ -50,7 +50,7 @@
} else if (type == CPU_TYPE_X86) { } else if (type == CPU_TYPE_X86) {
[cpu appendString:@"x86"]; [cpu appendString:@"x86"];
switch(subtype) { switch (subtype) {
case CPU_SUBTYPE_X86_64_H: case CPU_SUBTYPE_X86_64_H:
[cpu appendString:@"_64h"]; [cpu appendString:@"_64h"];
break; break;
@ -68,23 +68,23 @@
} else if (type == CPU_TYPE_ARM64) { } else if (type == CPU_TYPE_ARM64) {
[cpu appendString:@"arm64"]; [cpu appendString:@"arm64"];
switch(subtype) { switch (subtype) {
case CPU_SUBTYPE_ARM64_V8: case CPU_SUBTYPE_ARM64_V8:
[cpu appendString:@"v8"]; [cpu appendString:@"v8"];
break; break;
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120100 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= 120100
case CPU_SUBTYPE_ARM64E: case CPU_SUBTYPE_ARM64E:
[cpu appendString:@"e"]; [cpu appendString:@"e"];
break; break;
#endif #endif
} }
} else if (type == CPU_TYPE_ARM) { } else if (type == CPU_TYPE_ARM) {
[cpu appendString:@"arm"]; [cpu appendString:@"arm"];
switch(subtype) { switch (subtype) {
case CPU_SUBTYPE_ARM_XSCALE: case CPU_SUBTYPE_ARM_XSCALE:
[cpu appendString:@"xscale"]; [cpu appendString:@"xscale"];
break; break;
@ -125,16 +125,17 @@
#endif #endif
} }
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 120100 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= 120100
} else if (type == CPU_TYPE_ARM64_32) { } else if (type == CPU_TYPE_ARM64_32) {
[cpu appendString:@"arm64_32"]; [cpu appendString:@"arm64_32"];
switch(subtype) { switch (subtype) {
case CPU_SUBTYPE_ARM64_32_V8: case CPU_SUBTYPE_ARM64_32_V8:
[cpu appendString:@"v8"]; [cpu appendString:@"v8"];
break; break;
} }
#endif #endif
} else { } else {
[cpu appendString:[NSString stringWithFormat:@"%d", type]]; [cpu appendString:[NSString stringWithFormat:@"%d", type]];
@ -143,7 +144,7 @@
return cpu; return cpu;
} }
+ (NSString*)getArch { + (NSString *)getArch {
NSMutableString *arch = [[NSMutableString alloc] init]; NSMutableString *arch = [[NSMutableString alloc] init];
#ifdef FFMPEG_KIT_ARMV7 #ifdef FFMPEG_KIT_ARMV7

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

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

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

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

@ -17,12 +17,12 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>. * along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/ */
#import "fftools_ffmpeg.h" #import "FFmpegKit.h"
#import "ArchDetect.h" #import "ArchDetect.h"
#import "AtomicLong.h" #import "AtomicLong.h"
#import "FFmpegKit.h"
#import "FFmpegKitConfig.h" #import "FFmpegKitConfig.h"
#import "Packages.h" #import "Packages.h"
#import "fftools_ffmpeg.h"
@implementation FFmpegKit @implementation FFmpegKit
@ -31,65 +31,113 @@
[FFmpegKitConfig class]; [FFmpegKitConfig class];
NSLog(@"Loaded ffmpeg-kit-%@-%@-%@-%@.\n", [Packages getPackageName], [ArchDetect getArch], [FFmpegKitConfig getVersion], [FFmpegKitConfig getBuildDate]); NSLog(@"Loaded ffmpeg-kit-%@-%@-%@-%@.\n", [Packages getPackageName],
[ArchDetect getArch], [FFmpegKitConfig getVersion],
[FFmpegKitConfig getBuildDate]);
} }
+ (FFmpegSession*)executeWithArguments:(NSArray*)arguments { + (FFmpegSession *)executeWithArguments:(NSArray *)arguments {
FFmpegSession* session = [FFmpegSession create:arguments]; FFmpegSession *session = [FFmpegSession create:arguments];
[FFmpegKitConfig ffmpegExecute:session]; [FFmpegKitConfig ffmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback { + (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback]; withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session]; [FFmpegKitConfig asyncFFmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback { + (FFmpegSession *)
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback]; executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session]; [FFmpegKitConfig asyncFFmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFmpegSession *)executeWithArgumentsAsync:(NSArray *)arguments
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback]; withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFmpegSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFmpegSession *)
FFmpegSession* session = [FFmpegSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback]; executeWithArgumentsAsync:(NSArray *)arguments
withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session = [FFmpegSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFmpegSession*)execute:(NSString*)command { + (FFmpegSession *)execute:(NSString *)command {
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command]]; FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig ffmpegExecute:session]; [FFmpegKitConfig ffmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback { + (FFmpegSession *)executeAsync:(NSString *)command
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback]; withCompleteCallback:
(FFmpegSessionCompleteCallback)completeCallback {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session]; [FFmpegKitConfig asyncFFmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback { + (FFmpegSession *)executeAsync:(NSString *)command
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback]; withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session]; [FFmpegKitConfig asyncFFmpegExecute:session];
return session; return session;
} }
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFmpegSession *)executeAsync:(NSString *)command
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback]; withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFmpegSession*)executeAsync:(NSString*)command withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withStatisticsCallback:(StatisticsCallback)statisticsCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFmpegSession *)executeAsync:(NSString *)command
FFmpegSession* session = [FFmpegSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback withStatisticsCallback:statisticsCallback]; withCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
withStatisticsCallback:(StatisticsCallback)statisticsCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFmpegSession *session =
[FFmpegSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback
withStatisticsCallback:statisticsCallback];
[FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFmpegExecute:session onDispatchQueue:queue];
return session; return session;
} }
@ -98,7 +146,8 @@
/* /*
* ZERO (0) IS A SPECIAL SESSION ID * ZERO (0) IS A SPECIAL SESSION ID
* WHEN IT IS PASSED TO THIS METHOD, A SIGINT IS GENERATED WHICH CANCELS ALL ONGOING SESSIONS * WHEN IT IS PASSED TO THIS METHOD, A SIGINT IS GENERATED WHICH CANCELS ALL
* ONGOING SESSIONS
*/ */
cancel_operation(0); cancel_operation(0);
} }
@ -107,7 +156,7 @@
cancel_operation(sessionId); cancel_operation(sessionId);
} }
+ (NSArray*)listSessions { + (NSArray *)listSessions {
return [FFmpegKitConfig getFFmpegSessions]; return [FFmpegKitConfig getFFmpegSessions];
} }

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

File diff suppressed because it is too large Load Diff

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

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

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

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

@ -17,10 +17,10 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>. * along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/ */
#import "fftools_ffmpeg.h" #import "FFprobeKit.h"
#import "FFmpegKit.h" #import "FFmpegKit.h"
#import "FFmpegKitConfig.h" #import "FFmpegKitConfig.h"
#import "FFprobeKit.h" #import "fftools_ffmpeg.h"
@implementation FFprobeKit @implementation FFprobeKit
@ -28,135 +28,257 @@
[FFmpegKitConfig class]; [FFmpegKitConfig class];
} }
+ (NSArray*)defaultGetMediaInformationCommandArguments:(NSString*)path { + (NSArray *)defaultGetMediaInformationCommandArguments:(NSString *)path {
return [[NSArray alloc] initWithObjects:@"-v", @"error", @"-hide_banner", @"-print_format", @"json", @"-show_format", @"-show_streams", @"-show_chapters", @"-i", path, nil]; return [[NSArray alloc]
initWithObjects:@"-v", @"error", @"-hide_banner", @"-print_format",
@"json", @"-show_format", @"-show_streams",
@"-show_chapters", @"-i", path, nil];
} }
+ (FFprobeSession*)executeWithArguments:(NSArray*)arguments { + (FFprobeSession *)executeWithArguments:(NSArray *)arguments {
FFprobeSession* session = [FFprobeSession create:arguments]; FFprobeSession *session = [FFprobeSession create:arguments];
[FFmpegKitConfig ffprobeExecute:session]; [FFmpegKitConfig ffprobeExecute:session];
return session; return session;
} }
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback { + (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback]; withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session]; [FFmpegKitConfig asyncFFprobeExecute:session];
return session; return session;
} }
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback { + (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback]; withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session]; [FFmpegKitConfig asyncFFprobeExecute:session];
return session; return session;
} }
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback]; withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFprobeSession*)executeWithArgumentsAsync:(NSArray*)arguments withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFprobeSession *)executeWithArgumentsAsync:(NSArray *)arguments
FFprobeSession* session = [FFprobeSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback]; withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session = [FFprobeSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFprobeSession*)execute:(NSString*)command { + (FFprobeSession *)execute:(NSString *)command {
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command]]; FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig ffprobeExecute:session]; [FFmpegKitConfig ffprobeExecute:session];
return session; return session;
} }
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback { + (FFprobeSession *)executeAsync:(NSString *)command
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback]; withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session]; [FFmpegKitConfig asyncFFprobeExecute:session];
return session; return session;
} }
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback { + (FFprobeSession *)executeAsync:(NSString *)command
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback]; withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session]; [FFmpegKitConfig asyncFFprobeExecute:session];
return session; return session;
} }
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFprobeSession *)executeAsync:(NSString *)command
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback]; withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (FFprobeSession*)executeAsync:(NSString*)command withCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue { + (FFprobeSession *)executeAsync:(NSString *)command
FFprobeSession* session = [FFprobeSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback]; withCompleteCallback:
(FFprobeSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue {
FFprobeSession *session =
[FFprobeSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue]; [FFmpegKitConfig asyncFFprobeExecute:session onDispatchQueue:queue];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformation:(NSString*)path { + (MediaInformationSession *)getMediaInformation:(NSString *)path {
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path]; NSArray *arguments =
MediaInformationSession* session = [MediaInformationSession create:arguments]; [FFprobeKit defaultGetMediaInformationCommandArguments:path];
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit]; MediaInformationSession *session =
[MediaInformationSession create:arguments];
[FFmpegKitConfig
getMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformation:(NSString*)path withTimeout:(int)waitTimeout { + (MediaInformationSession *)getMediaInformation:(NSString *)path
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path]; withTimeout:(int)waitTimeout {
MediaInformationSession* session = [MediaInformationSession create:arguments]; NSArray *arguments =
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:waitTimeout]; [FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments];
[FFmpegKitConfig getMediaInformationExecute:session
withTimeout:waitTimeout];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback { + (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path]; withCompleteCallback:
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback]; (MediaInformationSessionCompleteCallback)
[FFmpegKitConfig asyncGetMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit]; completeCallback {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig
asyncGetMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback withTimeout:(int)waitTimeout { + (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path]; withCompleteCallback:
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback]; (MediaInformationSessionCompleteCallback)
[FFmpegKitConfig asyncGetMediaInformationExecute:session withTimeout:waitTimeout]; completeCallback
withLogCallback:(LogCallback)logCallback
withTimeout:(int)waitTimeout {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
withTimeout:waitTimeout];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback onDispatchQueue:(dispatch_queue_t)queue { + (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path]; withCompleteCallback:
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback]; (MediaInformationSessionCompleteCallback)
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit]; completeCallback
onDispatchQueue:(dispatch_queue_t)queue {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig
asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformationAsync:(NSString*)path withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout { + (MediaInformationSession *)getMediaInformationAsync:(NSString *)path
NSArray* arguments = [FFprobeKit defaultGetMediaInformationCommandArguments:path]; withCompleteCallback:
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback]; (MediaInformationSessionCompleteCallback)
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout]; completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
NSArray *arguments =
[FFprobeKit defaultGetMediaInformationCommandArguments:path];
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformationFromCommand:(NSString*)command { + (MediaInformationSession *)getMediaInformationFromCommand:
MediaInformationSession* session = [MediaInformationSession create:[FFmpegKitConfig parseArguments:command]]; (NSString *)command {
[FFmpegKitConfig getMediaInformationExecute:session withTimeout:AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit]; MediaInformationSession *session = [MediaInformationSession
create:[FFmpegKitConfig parseArguments:command]];
[FFmpegKitConfig
getMediaInformationExecute:session
withTimeout:
AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformationFromCommandAsync:(NSString*)command withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout { + (MediaInformationSession *)
MediaInformationSession* session = [MediaInformationSession create:[FFmpegKitConfig parseArguments:command] withCompleteCallback:completeCallback withLogCallback:logCallback]; getMediaInformationFromCommandAsync:(NSString *)command
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout]; withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
MediaInformationSession *session =
[MediaInformationSession create:[FFmpegKitConfig parseArguments:command]
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session; return session;
} }
+ (MediaInformationSession*)getMediaInformationFromCommandArgumentsAsync:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout { + (MediaInformationSession *)
MediaInformationSession* session = [MediaInformationSession create:arguments withCompleteCallback:completeCallback withLogCallback:logCallback]; getMediaInformationFromCommandArgumentsAsync:(NSArray *)arguments
[FFmpegKitConfig asyncGetMediaInformationExecute:session onDispatchQueue:queue withTimeout:waitTimeout]; withCompleteCallback:
(MediaInformationSessionCompleteCallback)
completeCallback
withLogCallback:(LogCallback)logCallback
onDispatchQueue:(dispatch_queue_t)queue
withTimeout:(int)waitTimeout {
MediaInformationSession *session =
[MediaInformationSession create:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
[FFmpegKitConfig asyncGetMediaInformationExecute:session
onDispatchQueue:queue
withTimeout:waitTimeout];
return session; return session;
} }
+ (NSArray*)listFFprobeSessions { + (NSArray *)listFFprobeSessions {
return [FFmpegKitConfig getFFprobeSessions]; return [FFmpegKitConfig getFFprobeSessions];
} }
+ (NSArray*)listMediaInformationSessions { + (NSArray *)listMediaInformationSessions {
return [FFmpegKitConfig getMediaInformationSessions]; return [FFmpegKitConfig getMediaInformationSessions];
} }

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

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

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

@ -27,8 +27,9 @@
typedef NS_ENUM(NSInteger, Level) { typedef NS_ENUM(NSInteger, Level) {
/** /**
* This log level is defined by FFmpegKit. It is used to specify logs printed to stderr by * This log level is defined by FFmpegKit. It is used to specify logs
* FFmpeg. Logs that has this level are not filtered and always redirected. * printed to stderr by FFmpeg. Logs that has this level are not filtered
* and always redirected.
*/ */
LevelAVLogStdErr = -16, LevelAVLogStdErr = -16,

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

@ -19,46 +19,61 @@
#import "MediaInformationJsonParser.h" #import "MediaInformationJsonParser.h"
NSString* const MediaInformationJsonParserKeyStreams = @"streams"; NSString *const MediaInformationJsonParserKeyStreams = @"streams";
NSString* const MediaInformationJsonParserKeyChapters = @"chapters"; NSString *const MediaInformationJsonParserKeyChapters = @"chapters";
@implementation MediaInformationJsonParser @implementation MediaInformationJsonParser
+ (MediaInformation*)from:(NSString*)ffprobeJsonOutput { + (MediaInformation *)from:(NSString *)ffprobeJsonOutput {
@try { @try {
return [self fromWithError:ffprobeJsonOutput]; return [self fromWithError:ffprobeJsonOutput];
} @catch (NSException *exception) { } @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; return nil;
} }
} }
+ (MediaInformation*)fromWithError:(NSString*)ffprobeJsonOutput { + (MediaInformation *)fromWithError:(NSString *)ffprobeJsonOutput {
NSError* error = nil; NSError *error = nil;
NSData *jsonData = [ffprobeJsonOutput dataUsingEncoding:NSUTF8StringEncoding]; NSData *jsonData =
NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error]; [ffprobeJsonOutput dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDictionary =
[NSJSONSerialization JSONObjectWithData:jsonData
options:kNilOptions
error:&error];
if (error != nil) { 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) { if (jsonDictionary == nil) {
return nil; return nil;
} }
NSArray* jsonStreamArray = [jsonDictionary objectForKey:MediaInformationJsonParserKeyStreams]; NSArray *jsonStreamArray =
[jsonDictionary objectForKey:MediaInformationJsonParserKeyStreams];
NSMutableArray *streamArray = [[NSMutableArray alloc] init]; NSMutableArray *streamArray = [[NSMutableArray alloc] init];
for(int i = 0; i<jsonStreamArray.count; i++) { for (int i = 0; i < jsonStreamArray.count; i++) {
NSDictionary *streamDictionary = [jsonStreamArray objectAtIndex: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]; NSMutableArray *chapterArray = [[NSMutableArray alloc] init];
for(int i = 0; i<jsonChapterArray.count; i++) { for (int i = 0; i < jsonChapterArray.count; i++) {
NSDictionary *chapterDictionary = [jsonChapterArray objectAtIndex:i]; NSDictionary *chapterDictionary = [jsonChapterArray objectAtIndex:i];
[chapterArray addObject:[[Chapter alloc] init:chapterDictionary]]; [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 @end

@ -20,14 +20,14 @@
#ifndef FFMPEG_KIT_MEDIA_INFORMATION_SESSION_H #ifndef FFMPEG_KIT_MEDIA_INFORMATION_SESSION_H
#define FFMPEG_KIT_MEDIA_INFORMATION_SESSION_H #define FFMPEG_KIT_MEDIA_INFORMATION_SESSION_H
#import <Foundation/Foundation.h>
#import "AbstractSession.h" #import "AbstractSession.h"
#import "MediaInformation.h" #import "MediaInformation.h"
#import "MediaInformationSessionCompleteCallback.h" #import "MediaInformationSessionCompleteCallback.h"
#import <Foundation/Foundation.h>
/** /**
* <p>A custom FFprobe session, which produces a <code>MediaInformation</code> object using the * <p>A custom FFprobe session, which produces a <code>MediaInformation</code>
* FFprobe output. * object using the FFprobe output.
*/ */
@interface MediaInformationSession : AbstractSession @interface MediaInformationSession : AbstractSession
@ -37,7 +37,7 @@
* @param arguments command arguments * @param arguments command arguments
* @return created session * @return created session
*/ */
+ (instancetype)create:(NSArray*)arguments; + (instancetype)create:(NSArray *)arguments;
/** /**
* Creates a new media information session. * Creates a new media information session.
@ -46,7 +46,9 @@
* @param completeCallback session specific complete callback * @param completeCallback session specific complete callback
* @return created session * @return created session
*/ */
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback; + (instancetype)create:(NSArray *)arguments
withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback;
/** /**
* Creates a new media information session. * Creates a new media information session.
@ -56,22 +58,25 @@
* @param logCallback session specific log callback * @param logCallback session specific log callback
* @return created session * @return created session
*/ */
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback; + (instancetype)create:(NSArray *)arguments
withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback;
/** /**
* Returns the media information extracted in this session. * Returns the media information extracted in this session.
* *
* @return media information extracted or nil if the command failed or the output can not be * @return media information extracted or nil if the command failed or the
* parsed * output can not be parsed
*/ */
- (MediaInformation*)getMediaInformation; - (MediaInformation *)getMediaInformation;
/** /**
* Sets the media information extracted in this session. * Sets the media information extracted in this session.
* *
* @param mediaInformation media information extracted * @param mediaInformation media information extracted
*/ */
- (void)setMediaInformation:(MediaInformation*)mediaInformation; - (void)setMediaInformation:(MediaInformation *)mediaInformation;
/** /**
* Returns the session specific complete callback. * Returns the session specific complete callback.

@ -22,7 +22,7 @@
#import "MediaInformation.h" #import "MediaInformation.h"
@implementation MediaInformationSession { @implementation MediaInformationSession {
MediaInformation* _mediaInformation; MediaInformation *_mediaInformation;
MediaInformationSessionCompleteCallback _completeCallback; MediaInformationSessionCompleteCallback _completeCallback;
} }
@ -30,21 +30,37 @@
// EMPTY INITIALIZE // EMPTY INITIALIZE
} }
+ (instancetype)create:(NSArray*)arguments { + (instancetype)create:(NSArray *)arguments {
return [[self alloc] init:arguments withCompleteCallback:nil withLogCallback:nil]; return [[self alloc] init:arguments
withCompleteCallback:nil
withLogCallback:nil];
} }
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback { + (instancetype)create:(NSArray *)arguments
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:nil]; withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:nil];
} }
+ (instancetype)create:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback { + (instancetype)create:(NSArray *)arguments
return [[self alloc] init:arguments withCompleteCallback:completeCallback withLogCallback:logCallback]; withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
return [[self alloc] init:arguments
withCompleteCallback:completeCallback
withLogCallback:logCallback];
} }
- (instancetype)init:(NSArray*)arguments withCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback withLogCallback:(LogCallback)logCallback { - (instancetype)init:(NSArray *)arguments
withCompleteCallback:
(MediaInformationSessionCompleteCallback)completeCallback
withLogCallback:(LogCallback)logCallback {
self = [super init:arguments withLogCallback:logCallback withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; self = [super init:arguments
withLogCallback:logCallback
withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs];
if (self) { if (self) {
_completeCallback = completeCallback; _completeCallback = completeCallback;
@ -53,11 +69,11 @@
return self; return self;
} }
- (MediaInformation*)getMediaInformation { - (MediaInformation *)getMediaInformation {
return _mediaInformation; return _mediaInformation;
} }
- (void)setMediaInformation:(MediaInformation*)mediaInformation { - (void)setMediaInformation:(MediaInformation *)mediaInformation {
_mediaInformation = mediaInformation; _mediaInformation = mediaInformation;
} }
@ -78,4 +94,3 @@
} }
@end @end

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

@ -32,14 +32,14 @@
* *
* @return predicted FFmpegKit binary package name * @return predicted FFmpegKit binary package name
*/ */
+ (NSString*)getPackageName; + (NSString *)getPackageName;
/** /**
* Returns enabled external libraries by FFmpeg. * Returns enabled external libraries by FFmpeg.
* *
* @return enabled external libraries * @return enabled external libraries
*/ */
+ (NSArray*)getExternalLibraries; + (NSArray *)getExternalLibraries;
@end @end

@ -17,10 +17,10 @@
* along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>. * along with FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
*/ */
#import "Packages.h"
#import "FFmpegKitConfig.h"
#import "config.h" #import "config.h"
#import "libavutil/ffversion.h" #import "libavutil/ffversion.h"
#import "FFmpegKitConfig.h"
#import "Packages.h"
static NSMutableArray *supportedExternalLibraries; static NSMutableArray *supportedExternalLibraries;
@ -62,11 +62,11 @@ static NSMutableArray *supportedExternalLibraries;
[supportedExternalLibraries addObject:@"xvid"]; [supportedExternalLibraries addObject:@"xvid"];
} }
+ (NSString*)getBuildConf { + (NSString *)getBuildConf {
return [NSString stringWithUTF8String:FFMPEG_CONFIGURATION]; return [NSString stringWithUTF8String:FFMPEG_CONFIGURATION];
} }
+ (NSString*)extractPackageNameFromExternalLibraries { + (NSString *)extractPackageNameFromExternalLibraries {
NSArray *enabledLibraryArray = [Packages getExternalLibraries]; NSArray *enabledLibraryArray = [Packages getExternalLibraries];
Boolean speex = [enabledLibraryArray containsObject:@"speex"]; Boolean speex = [enabledLibraryArray containsObject:@"speex"];
Boolean fribidi = [enabledLibraryArray containsObject:@"fribidi"]; Boolean fribidi = [enabledLibraryArray containsObject:@"fribidi"];
@ -237,25 +237,31 @@ static NSMutableArray *supportedExternalLibraries;
return @"min"; return @"min";
} }
+ (NSString*)getPackageName { + (NSString *)getPackageName {
#ifdef FFMPEG_KIT_PACKAGE #ifdef FFMPEG_KIT_PACKAGE
return [NSString stringWithUTF8String:FFMPEG_KIT_PACKAGE]; return [NSString stringWithUTF8String:FFMPEG_KIT_PACKAGE];
#else #else
return [self extractPackageNameFromExternalLibraries]; return [self extractPackageNameFromExternalLibraries];
#endif #endif
} }
+ (NSArray*)getExternalLibraries { + (NSArray *)getExternalLibraries {
NSString *buildConfiguration = [Packages getBuildConf]; NSString *buildConfiguration = [Packages getBuildConf];
NSMutableArray *enabledLibraryArray = [[NSMutableArray alloc] init]; NSMutableArray *enabledLibraryArray = [[NSMutableArray alloc] init];
for (int i=0; i < [supportedExternalLibraries count]; i++) { for (int i = 0; i < [supportedExternalLibraries count]; i++) {
NSString *supportedExternalLibrary = [supportedExternalLibraries objectAtIndex:i]; NSString *supportedExternalLibrary =
[supportedExternalLibraries objectAtIndex:i];
NSString *libraryName1 = [NSString stringWithFormat:@"enable-%@", supportedExternalLibrary]; NSString *libraryName1 =
NSString *libraryName2 = [NSString stringWithFormat:@"enable-lib%@", supportedExternalLibrary]; [NSString stringWithFormat:@"enable-%@", supportedExternalLibrary];
NSString *libraryName2 = [NSString
stringWithFormat:@"enable-lib%@", supportedExternalLibrary];
if ([buildConfiguration rangeOfString:libraryName1].location != NSNotFound || [buildConfiguration rangeOfString:libraryName2].location != NSNotFound) { if ([buildConfiguration rangeOfString:libraryName1].location !=
NSNotFound ||
[buildConfiguration rangeOfString:libraryName2].location !=
NSNotFound) {
[enabledLibraryArray addObject:supportedExternalLibrary]; [enabledLibraryArray addObject:supportedExternalLibrary];
} }
} }

@ -31,9 +31,9 @@ typedef NS_ENUM(NSUInteger, ReturnCodeEnum) {
- (instancetype)init:(int)value; - (instancetype)init:(int)value;
+ (BOOL)isSuccess:(ReturnCode*)value; + (BOOL)isSuccess:(ReturnCode *)value;
+ (BOOL)isCancel:(ReturnCode*)value; + (BOOL)isCancel:(ReturnCode *)value;
- (int)getValue; - (int)getValue;

@ -32,11 +32,11 @@
return self; return self;
} }
+ (BOOL)isSuccess:(ReturnCode*)value { + (BOOL)isSuccess:(ReturnCode *)value {
return (value != nil) && ([value getValue] == ReturnCodeSuccess); return (value != nil) && ([value getValue] == ReturnCodeSuccess);
} }
+ (BOOL)isCancel:(ReturnCode*)value { + (BOOL)isCancel:(ReturnCode *)value {
return (value != nil) && ([value getValue] == ReturnCodeCancel); return (value != nil) && ([value getValue] == ReturnCodeCancel);
} }
@ -56,7 +56,7 @@
return (_value == ReturnCodeCancel); return (_value == ReturnCodeCancel);
} }
- (NSString*)description { - (NSString *)description {
return [NSString stringWithFormat:@"%d", _value]; return [NSString stringWithFormat:@"%d", _value];
} }

@ -20,12 +20,12 @@
#ifndef FFMPEG_KIT_SESSION_H #ifndef FFMPEG_KIT_SESSION_H
#define FFMPEG_KIT_SESSION_H #define FFMPEG_KIT_SESSION_H
#import <Foundation/Foundation.h>
#import "Log.h" #import "Log.h"
#import "LogCallback.h" #import "LogCallback.h"
#import "LogRedirectionStrategy.h" #import "LogRedirectionStrategy.h"
#import "ReturnCode.h" #import "ReturnCode.h"
#import "SessionState.h" #import "SessionState.h"
#import <Foundation/Foundation.h>
/** /**
* <p>Common interface for all <code>FFmpegKit</code> sessions. * <p>Common interface for all <code>FFmpegKit</code> sessions.
@ -53,27 +53,27 @@
* *
* @return session create time * @return session create time
*/ */
- (NSDate*)getCreateTime; - (NSDate *)getCreateTime;
/** /**
* Returns session start time. * Returns session start time.
* *
* @return session start time * @return session start time
*/ */
- (NSDate*)getStartTime; - (NSDate *)getStartTime;
/** /**
* Returns session end time. * Returns session end time.
* *
* @return session end time * @return session end time
*/ */
- (NSDate*)getEndTime; - (NSDate *)getEndTime;
/** /**
* Returns the time taken to execute this session. * Returns the time taken to execute this session.
* *
* @return time taken to execute this session in milliseconds or zero (0) if the session is * @return time taken to execute this session in milliseconds or zero (0) if the
* not over yet * session is not over yet
*/ */
- (long)getDuration; - (long)getDuration;
@ -82,23 +82,24 @@
* *
* @return command arguments as an array * @return command arguments as an array
*/ */
- (NSArray*)getArguments; - (NSArray *)getArguments;
/** /**
* Returns command arguments as a concatenated string. * Returns command arguments as a concatenated string.
* *
* @return command arguments as a concatenated string * @return command arguments as a concatenated string
*/ */
- (NSString*)getCommand; - (NSString *)getCommand;
/** /**
* Returns all log entries generated for this session. If there are asynchronous * Returns all log entries generated for this session. If there are asynchronous
* messages that are not delivered yet, this method waits for them until the given timeout. * messages that are not delivered yet, this method waits for them until the
* given timeout.
* *
* @param waitTimeout wait timeout for asynchronous messages in milliseconds * @param waitTimeout wait timeout for asynchronous messages in milliseconds
* @return list of log entries generated for this session * @return list of log entries generated for this session
*/ */
- (NSArray*)getAllLogsWithTimeout:(int)waitTimeout; - (NSArray *)getAllLogsWithTimeout:(int)waitTimeout;
/** /**
* Returns all log entries generated for this session. If there are asynchronous * Returns all log entries generated for this session. If there are asynchronous
@ -106,50 +107,51 @@
* *
* @return list of log entries generated for this session * @return list of log entries generated for this session
*/ */
- (NSArray*)getAllLogs; - (NSArray *)getAllLogs;
/** /**
* Returns all log entries delivered for this session. Note that if there are asynchronous * Returns all log entries delivered for this session. Note that if there are
* messages that are not delivered yet, this method will not wait for them and will return * asynchronous messages that are not delivered yet, this method will not wait
* immediately. * for them and will return immediately.
* *
* @return list of log entries received for this session * @return list of log entries received for this session
*/ */
- (NSArray*)getLogs; - (NSArray *)getLogs;
/** /**
* Returns all log entries generated for this session as a concatenated string. If there are * Returns all log entries generated for this session as a concatenated string.
* asynchronous messages that are not delivered yet, this method waits for them until * If there are asynchronous messages that are not delivered yet, this method
* the given timeout. * waits for them until the given timeout.
* *
* @param waitTimeout wait timeout for asynchronous messages in milliseconds * @param waitTimeout wait timeout for asynchronous messages in milliseconds
* @return all log entries generated for this session as a concatenated string * @return all log entries generated for this session as a concatenated string
*/ */
- (NSString*)getAllLogsAsStringWithTimeout:(int)waitTimeout; - (NSString *)getAllLogsAsStringWithTimeout:(int)waitTimeout;
/** /**
* Returns all log entries generated for this session as a concatenated string. If there are * Returns all log entries generated for this session as a concatenated string.
* asynchronous messages that are not delivered yet, this method waits for them. * If there are asynchronous messages that are not delivered yet, this method
* waits for them.
* *
* @return all log entries generated for this session as a concatenated string * @return all log entries generated for this session as a concatenated string
*/ */
- (NSString*)getAllLogsAsString; - (NSString *)getAllLogsAsString;
/** /**
* Returns all log entries delivered for this session as a concatenated string. Note that if * Returns all log entries delivered for this session as a concatenated string.
* there are asynchronous messages that are not delivered yet, this method will not wait * Note that if there are asynchronous messages that are not delivered yet, this
* for them and will return immediately. * method will not wait for them and will return immediately.
* *
* @return list of log entries received for this session * @return list of log entries received for this session
*/ */
- (NSString*)getLogsAsString; - (NSString *)getLogsAsString;
/** /**
* Returns the log output generated while running the session. * Returns the log output generated while running the session.
* *
* @return log output generated * @return log output generated
*/ */
- (NSString*)getOutput; - (NSString *)getOutput;
/** /**
* Returns the state of the session. * Returns the state of the session.
@ -159,25 +161,25 @@
- (SessionState)getState; - (SessionState)getState;
/** /**
* Returns the return code for this session. Note that return code is only set for sessions * Returns the return code for this session. Note that return code is only set
* that end with SessionStateCompleted state. If a session is not started, still running or failed then * for sessions that end with SessionStateCompleted state. If a session is not
* this method returns nil. * started, still running or failed then this method returns nil.
* *
* @return the return code for this session if the session has completed, nil if session is * @return the return code for this session if the session has completed, nil if
* not started, still running or failed * session is not started, still running or failed
*/ */
- (ReturnCode*)getReturnCode; - (ReturnCode *)getReturnCode;
/** /**
* Returns the stack trace of the exception received while executing this session. * Returns the stack trace of the exception received while executing this
* <p> * session. <p> The stack trace is only set for sessions that end with
* The stack trace is only set for sessions that end with SessionStateFailed state. For sessions that has * SessionStateFailed state. For sessions that has SessionStateCompleted state
* SessionStateCompleted state this method returns nil. * this method returns nil.
* *
* @return stack trace of the exception received while executing this session, nil if session * @return stack trace of the exception received while executing this session,
* is not started, still running or completed * nil if session is not started, still running or completed
*/ */
- (NSString*)getFailStackTrace; - (NSString *)getFailStackTrace;
/** /**
* Returns session specific log redirection strategy. * Returns session specific log redirection strategy.
@ -187,23 +189,23 @@
- (LogRedirectionStrategy)getLogRedirectionStrategy; - (LogRedirectionStrategy)getLogRedirectionStrategy;
/** /**
* Returns whether there are still asynchronous messages being transmitted for this * Returns whether there are still asynchronous messages being transmitted for
* session or not. * this session or not.
* *
* @return true if there are still asynchronous messages being transmitted, false * @return true if there are still asynchronous messages being transmitted,
* otherwise * false otherwise
*/ */
- (BOOL)thereAreAsynchronousMessagesInTransmit; - (BOOL)thereAreAsynchronousMessagesInTransmit;
/** /**
* Adds a new log entry for this session. * Adds a new log entry for this session.
* *
* It is invoked internally by <code>FFmpegKit</code> library methods. Must not be used by user * It is invoked internally by <code>FFmpegKit</code> library methods. Must not
* applications. * be used by user applications.
* *
* @param log log entry * @param log log entry
*/ */
- (void)addLog:(Log*)log; - (void)addLog:(Log *)log;
/** /**
* Starts running the session. * Starts running the session.
@ -215,14 +217,14 @@
* *
* @param returnCode return code of the execution * @param returnCode return code of the execution
*/ */
- (void)complete:(ReturnCode*)returnCode; - (void)complete:(ReturnCode *)returnCode;
/** /**
* Ends running the session with a failure. * Ends running the session with a failure.
* *
* @param exception execution received * @param exception execution received
*/ */
- (void)fail:(NSException*)exception; - (void)fail:(NSException *)exception;
/** /**
* Returns whether it is an <code>FFmpeg</code> session or not. * Returns whether it is an <code>FFmpeg</code> session or not.
@ -241,7 +243,8 @@
/** /**
* Returns whether it is a <code>MediaInformation</code> session or not. * Returns whether it is a <code>MediaInformation</code> session or not.
* *
* @return true if it is a <code>MediaInformation</code> session, false otherwise * @return true if it is a <code>MediaInformation</code> session, false
* otherwise
*/ */
- (BOOL)isMediaInformation; - (BOOL)isMediaInformation;

@ -27,7 +27,14 @@
*/ */
@interface Statistics : NSObject @interface Statistics : NSObject
- (instancetype)init:(long)sessionId videoFrameNumber:(int)videoFrameNumber videoFps:(float)videoFps videoQuality:(float)videoQuality size:(int64_t)size time:(double)time bitrate:(double)bitrate speed:(double)speed; - (instancetype)init:(long)sessionId
videoFrameNumber:(int)videoFrameNumber
videoFps:(float)videoFps
videoQuality:(float)videoQuality
size:(int64_t)size
time:(double)time
bitrate:(double)bitrate
speed:(double)speed;
- (long)getSessionId; - (long)getSessionId;

@ -30,7 +30,14 @@
double _speed; double _speed;
} }
- (instancetype)init:(long)sessionId videoFrameNumber:(int)videoFrameNumber videoFps:(float)videoFps videoQuality:(float)videoQuality size:(int64_t)size time:(double)time bitrate:(double)bitrate speed:(double)speed { - (instancetype)init:(long)sessionId
videoFrameNumber:(int)videoFrameNumber
videoFps:(float)videoFps
videoQuality:(float)videoQuality
size:(int64_t)size
time:(double)time
bitrate:(double)bitrate
speed:(double)speed {
self = [super init]; self = [super init];
if (self) { if (self) {
_sessionId = sessionId; _sessionId = sessionId;

@ -20,14 +20,15 @@
#ifndef FFMPEG_KIT_STATISTICS_CALLBACK_H #ifndef FFMPEG_KIT_STATISTICS_CALLBACK_H
#define FFMPEG_KIT_STATISTICS_CALLBACK_H #define FFMPEG_KIT_STATISTICS_CALLBACK_H
#import <Foundation/Foundation.h>
#import "Statistics.h" #import "Statistics.h"
#import <Foundation/Foundation.h>
/** /**
* <p>Callback that receives statistics generated for <code>FFmpegKit</code> sessions. * <p>Callback that receives statistics generated for <code>FFmpegKit</code>
* sessions.
* *
* @param statistics statistics entry * @param statistics statistics entry
*/ */
typedef void (^StatisticsCallback)(Statistics* statistics); typedef void (^StatisticsCallback)(Statistics *statistics);
#endif // FFMPEG_KIT_STATISTICS_CALLBACK_H #endif // FFMPEG_KIT_STATISTICS_CALLBACK_H

@ -22,185 +22,186 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
extern NSString* const StreamKeyIndex; extern NSString *const StreamKeyIndex;
extern NSString* const StreamKeyType; extern NSString *const StreamKeyType;
extern NSString* const StreamKeyCodec; extern NSString *const StreamKeyCodec;
extern NSString* const StreamKeyCodecLong; extern NSString *const StreamKeyCodecLong;
extern NSString* const StreamKeyFormat; extern NSString *const StreamKeyFormat;
extern NSString* const StreamKeyWidth; extern NSString *const StreamKeyWidth;
extern NSString* const StreamKeyHeight; extern NSString *const StreamKeyHeight;
extern NSString* const StreamKeyBitRate; extern NSString *const StreamKeyBitRate;
extern NSString* const StreamKeySampleRate; extern NSString *const StreamKeySampleRate;
extern NSString* const StreamKeySampleFormat; extern NSString *const StreamKeySampleFormat;
extern NSString* const StreamKeyChannelLayout; extern NSString *const StreamKeyChannelLayout;
extern NSString* const StreamKeySampleAspectRatio; extern NSString *const StreamKeySampleAspectRatio;
extern NSString* const StreamKeyDisplayAspectRatio; extern NSString *const StreamKeyDisplayAspectRatio;
extern NSString* const StreamKeyAverageFrameRate; extern NSString *const StreamKeyAverageFrameRate;
extern NSString* const StreamKeyRealFrameRate; extern NSString *const StreamKeyRealFrameRate;
extern NSString* const StreamKeyTimeBase; extern NSString *const StreamKeyTimeBase;
extern NSString* const StreamKeyCodecTimeBase; extern NSString *const StreamKeyCodecTimeBase;
extern NSString* const StreamKeyTags; extern NSString *const StreamKeyTags;
/** /**
* Stream information class. * Stream information class.
*/ */
@interface StreamInformation : NSObject @interface StreamInformation : NSObject
- (instancetype)init:(NSDictionary*)streamDictionary; - (instancetype)init:(NSDictionary *)streamDictionary;
/** /**
* Returns stream index. * Returns stream index.
* *
* @return stream index, starting from zero * @return stream index, starting from zero
*/ */
- (NSNumber*)getIndex; - (NSNumber *)getIndex;
/** /**
* Returns stream type. * Returns stream type.
* *
* @return stream type; audio or video * @return stream type; audio or video
*/ */
- (NSString*)getType; - (NSString *)getType;
/** /**
* Returns stream codec. * Returns stream codec.
* *
* @return stream codec * @return stream codec
*/ */
- (NSString*)getCodec; - (NSString *)getCodec;
/** /**
* Returns stream codec in long format. * Returns stream codec in long format.
* *
* @return stream codec with additional profile and mode information * @return stream codec with additional profile and mode information
*/ */
- (NSString*)getCodecLong; - (NSString *)getCodecLong;
/** /**
* Returns stream format. * Returns stream format.
* *
* @return stream format * @return stream format
*/ */
- (NSString*)getFormat; - (NSString *)getFormat;
/** /**
* Returns width. * Returns width.
* *
* @return width in pixels * @return width in pixels
*/ */
- (NSNumber*)getWidth; - (NSNumber *)getWidth;
/** /**
* Returns height. * Returns height.
* *
* @return height in pixels * @return height in pixels
*/ */
- (NSNumber*)getHeight; - (NSNumber *)getHeight;
/** /**
* Returns bitrate. * Returns bitrate.
* *
* @return bitrate in kb/s * @return bitrate in kb/s
*/ */
- (NSString*)getBitrate; - (NSString *)getBitrate;
/** /**
* Returns sample rate. * Returns sample rate.
* *
* @return sample rate in hz * @return sample rate in hz
*/ */
- (NSString*)getSampleRate; - (NSString *)getSampleRate;
/** /**
* Returns sample format. * Returns sample format.
* *
* @return sample format * @return sample format
*/ */
- (NSString*)getSampleFormat; - (NSString *)getSampleFormat;
/** /**
* Returns channel layout. * Returns channel layout.
* *
* @return channel layout * @return channel layout
*/ */
- (NSString*)getChannelLayout; - (NSString *)getChannelLayout;
/** /**
* Returns sample aspect ratio. * Returns sample aspect ratio.
* *
* @return sample aspect ratio * @return sample aspect ratio
*/ */
- (NSString*)getSampleAspectRatio; - (NSString *)getSampleAspectRatio;
/** /**
* Returns display aspect ratio. * Returns display aspect ratio.
* *
* @return display aspect ratio * @return display aspect ratio
*/ */
- (NSString*)getDisplayAspectRatio; - (NSString *)getDisplayAspectRatio;
/** /**
* Returns average frame rate. * Returns average frame rate.
* *
* @return average frame rate in fps * @return average frame rate in fps
*/ */
- (NSString*)getAverageFrameRate; - (NSString *)getAverageFrameRate;
/** /**
* Returns real frame rate. * Returns real frame rate.
* *
* @return real frame rate in tbr * @return real frame rate in tbr
*/ */
- (NSString*)getRealFrameRate; - (NSString *)getRealFrameRate;
/** /**
* Returns time base. * Returns time base.
* *
* @return time base in tbn * @return time base in tbn
*/ */
- (NSString*)getTimeBase; - (NSString *)getTimeBase;
/** /**
* Returns codec time base. * Returns codec time base.
* *
* @return codec time base in tbc * @return codec time base in tbc
*/ */
- (NSString*)getCodecTimeBase; - (NSString *)getCodecTimeBase;
/** /**
* Returns all tags. * Returns all tags.
* *
* @return tags dictionary * @return tags dictionary
*/ */
- (NSDictionary*)getTags; - (NSDictionary *)getTags;
/** /**
* Returns the stream property associated with the key. * Returns the stream property associated with the key.
* *
* @return stream property as string or nil if the key is not found * @return stream property as string or nil if the key is not found
*/ */
- (NSString*)getStringProperty:(NSString*)key; - (NSString *)getStringProperty:(NSString *)key;
/** /**
* Returns the stream property associated with the key. * Returns the stream property associated with the key.
* *
* @return stream property as number or nil if the key is not found * @return stream property as number or nil if the key is not found
*/ */
- (NSNumber*)getNumberProperty:(NSString*)key; - (NSNumber *)getNumberProperty:(NSString *)key;
/** /**
* Returns the stream property associated with the key. * Returns the stream property associated with the key.
* *
* @return stream property as id or nil if the key is not found * @return stream property as id or nil if the key is not found
*/ */
- (id)getProperty:(NSString*)key; - (id)getProperty:(NSString *)key;
/** /**
* Returns all stream properties defined. * Returns all stream properties defined.
* *
* @return all stream properties in a dictionary or nil if no properties are defined * @return all stream properties in a dictionary or nil if no properties are
*/ * defined
- (NSDictionary*)getAllProperties; */
- (NSDictionary *)getAllProperties;
@end @end

@ -19,24 +19,24 @@
#import "StreamInformation.h" #import "StreamInformation.h"
NSString* const StreamKeyIndex = @"index"; NSString *const StreamKeyIndex = @"index";
NSString* const StreamKeyType = @"codec_type"; NSString *const StreamKeyType = @"codec_type";
NSString* const StreamKeyCodec = @"codec_name"; NSString *const StreamKeyCodec = @"codec_name";
NSString* const StreamKeyCodecLong = @"codec_long_name"; NSString *const StreamKeyCodecLong = @"codec_long_name";
NSString* const StreamKeyFormat = @"pix_fmt"; NSString *const StreamKeyFormat = @"pix_fmt";
NSString* const StreamKeyWidth = @"width"; NSString *const StreamKeyWidth = @"width";
NSString* const StreamKeyHeight = @"height"; NSString *const StreamKeyHeight = @"height";
NSString* const StreamKeyBitRate = @"bit_rate"; NSString *const StreamKeyBitRate = @"bit_rate";
NSString* const StreamKeySampleRate = @"sample_rate"; NSString *const StreamKeySampleRate = @"sample_rate";
NSString* const StreamKeySampleFormat = @"sample_fmt"; NSString *const StreamKeySampleFormat = @"sample_fmt";
NSString* const StreamKeyChannelLayout = @"channel_layout"; NSString *const StreamKeyChannelLayout = @"channel_layout";
NSString* const StreamKeySampleAspectRatio = @"sample_aspect_ratio"; NSString *const StreamKeySampleAspectRatio = @"sample_aspect_ratio";
NSString* const StreamKeyDisplayAspectRatio = @"display_aspect_ratio"; NSString *const StreamKeyDisplayAspectRatio = @"display_aspect_ratio";
NSString* const StreamKeyAverageFrameRate = @"avg_frame_rate"; NSString *const StreamKeyAverageFrameRate = @"avg_frame_rate";
NSString* const StreamKeyRealFrameRate = @"r_frame_rate"; NSString *const StreamKeyRealFrameRate = @"r_frame_rate";
NSString* const StreamKeyTimeBase = @"time_base"; NSString *const StreamKeyTimeBase = @"time_base";
NSString* const StreamKeyCodecTimeBase = @"codec_time_base"; NSString *const StreamKeyCodecTimeBase = @"codec_time_base";
NSString* const StreamKeyTags = @"tags"; NSString *const StreamKeyTags = @"tags";
@implementation StreamInformation { @implementation StreamInformation {
@ -44,10 +44,9 @@ NSString* const StreamKeyTags = @"tags";
* Stores all properties. * Stores all properties.
*/ */
NSDictionary *dictionary; NSDictionary *dictionary;
} }
- (instancetype)init:(NSDictionary*)streamDictionary { - (instancetype)init:(NSDictionary *)streamDictionary {
self = [super init]; self = [super init];
if (self) { if (self) {
dictionary = streamDictionary; dictionary = streamDictionary;
@ -56,80 +55,80 @@ NSString* const StreamKeyTags = @"tags";
return self; return self;
} }
- (NSNumber*)getIndex { - (NSNumber *)getIndex {
return [self getNumberProperty:StreamKeyIndex]; return [self getNumberProperty:StreamKeyIndex];
} }
- (NSString*)getType { - (NSString *)getType {
return [self getStringProperty:StreamKeyType]; return [self getStringProperty:StreamKeyType];
} }
- (NSString*)getCodec { - (NSString *)getCodec {
return [self getStringProperty:StreamKeyCodec]; return [self getStringProperty:StreamKeyCodec];
} }
- (NSString*)getCodecLong { - (NSString *)getCodecLong {
return [self getStringProperty:StreamKeyCodecLong]; return [self getStringProperty:StreamKeyCodecLong];
} }
- (NSString*)getFormat { - (NSString *)getFormat {
return [self getStringProperty:StreamKeyFormat]; return [self getStringProperty:StreamKeyFormat];
} }
- (NSNumber*)getWidth { - (NSNumber *)getWidth {
return [self getNumberProperty:StreamKeyWidth]; return [self getNumberProperty:StreamKeyWidth];
} }
- (NSNumber*)getHeight { - (NSNumber *)getHeight {
return [self getNumberProperty:StreamKeyHeight]; return [self getNumberProperty:StreamKeyHeight];
} }
- (NSString*)getBitrate { - (NSString *)getBitrate {
return [self getStringProperty:StreamKeyBitRate]; return [self getStringProperty:StreamKeyBitRate];
} }
- (NSString*)getSampleRate { - (NSString *)getSampleRate {
return [self getStringProperty:StreamKeySampleRate]; return [self getStringProperty:StreamKeySampleRate];
} }
- (NSString*)getSampleFormat { - (NSString *)getSampleFormat {
return [self getStringProperty:StreamKeySampleFormat]; return [self getStringProperty:StreamKeySampleFormat];
} }
- (NSString*)getChannelLayout { - (NSString *)getChannelLayout {
return [self getStringProperty:StreamKeyChannelLayout]; return [self getStringProperty:StreamKeyChannelLayout];
} }
- (NSString*)getSampleAspectRatio { - (NSString *)getSampleAspectRatio {
return [self getStringProperty:StreamKeySampleAspectRatio]; return [self getStringProperty:StreamKeySampleAspectRatio];
} }
- (NSString*)getDisplayAspectRatio { - (NSString *)getDisplayAspectRatio {
return [self getStringProperty:StreamKeyDisplayAspectRatio]; return [self getStringProperty:StreamKeyDisplayAspectRatio];
} }
- (NSString*)getAverageFrameRate { - (NSString *)getAverageFrameRate {
return [self getStringProperty:StreamKeyAverageFrameRate]; return [self getStringProperty:StreamKeyAverageFrameRate];
} }
- (NSString*)getRealFrameRate { - (NSString *)getRealFrameRate {
return [self getStringProperty:StreamKeyRealFrameRate]; return [self getStringProperty:StreamKeyRealFrameRate];
} }
- (NSString*)getTimeBase { - (NSString *)getTimeBase {
return [self getStringProperty:StreamKeyTimeBase]; return [self getStringProperty:StreamKeyTimeBase];
} }
- (NSString*)getCodecTimeBase { - (NSString *)getCodecTimeBase {
return [self getStringProperty:StreamKeyCodecTimeBase]; return [self getStringProperty:StreamKeyCodecTimeBase];
} }
- (NSDictionary*)getTags { - (NSDictionary *)getTags {
return [self getProperty:StreamKeyTags]; return [self getProperty:StreamKeyTags];
} }
- (NSString*)getStringProperty:(NSString*)key { - (NSString *)getStringProperty:(NSString *)key {
NSDictionary* allProperties = [self getAllProperties]; NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) { if (allProperties == nil) {
return nil; return nil;
} }
@ -137,8 +136,8 @@ NSString* const StreamKeyTags = @"tags";
return allProperties[key]; return allProperties[key];
} }
- (NSNumber*)getNumberProperty:(NSString*)key { - (NSNumber *)getNumberProperty:(NSString *)key {
NSDictionary* allProperties = [self getAllProperties]; NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) { if (allProperties == nil) {
return nil; return nil;
} }
@ -146,8 +145,8 @@ NSString* const StreamKeyTags = @"tags";
return allProperties[key]; return allProperties[key];
} }
- (id)getProperty:(NSString*)key { - (id)getProperty:(NSString *)key {
NSDictionary* allProperties = [self getAllProperties]; NSDictionary *allProperties = [self getAllProperties];
if (allProperties == nil) { if (allProperties == nil) {
return nil; return nil;
} }
@ -155,7 +154,7 @@ NSString* const StreamKeyTags = @"tags";
return allProperties[key]; return allProperties[key];
} }
- (NSDictionary*)getAllProperties { - (NSDictionary *)getAllProperties {
return dictionary; return dictionary;
} }

@ -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,145 @@
/*
* 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
#if HAVE_TERMIOS_H
#include <termios.h>
#endif
#include "fftools_ffmpeg.h"
#include "libavformat/avio.h"
#include "libavutil/dict.h"
extern __thread BenchmarkTimeStamps current_time;
extern __thread struct termios oldtty;
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

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
/* /*
* This file is part of FFmpeg. * This file is part of FFmpeg.
* Copyright (c) 2018-2022 Taner Sener * 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 * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -19,17 +19,22 @@
*/ */
/* /*
* This file is the modified version of ffmpeg.h file living in ffmpeg source code under the fftools folder. We * This file is the modified version of ffmpeg.h file living in ffmpeg source
* manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied * code under the fftools folder. We manually update it each time we depend on a
* by us to develop mobile-ffmpeg and later ffmpeg-kit libraries. * new ffmpeg version. Below you can see the list of changes applied by us to
* develop mobile-ffmpeg and later ffmpeg-kit libraries.
* *
* ffmpeg-kit changes by ARTHENICA LTD * ffmpeg-kit changes by ARTHENICA LTD
* *
* 11.2024
* --------------------------------------------------------
* - FFmpeg 6.1 changes migrated
*
* 07.2023 * 07.2023
* -------------------------------------------------------- * --------------------------------------------------------
* - FFmpeg 6.0 changes migrated * - FFmpeg 6.0 changes migrated
* - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT, SPECIFIER_OPT_FMT declarations migrated to * - WARN_MULTIPLE_OPT_USAGE, MATCH_PER_STREAM_OPT, MATCH_PER_TYPE_OPT,
* ffmpeg_mux.h * SPECIFIER_OPT_FMT declarations migrated to ffmpeg_mux.h
* - "class" member field renamed as clazz * - "class" member field renamed as clazz
* - time field in set_report_callback updated as double * - time field in set_report_callback updated as double
* *
@ -39,7 +44,8 @@
* -------------------------------------------------------- * --------------------------------------------------------
* - config.h include added back * - config.h include added back
* - volatile dropped from thread local variables * - 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 * 06.2020
* -------------------------------------------------------- * --------------------------------------------------------
@ -47,8 +53,9 @@
* *
* 12.2019 * 12.2019
* -------------------------------------------------------- * --------------------------------------------------------
* - concurrent execution support ("__thread" specifier added to variables used by multiple threads, * - concurrent execution support ("__thread" specifier added to variables used
* signatures of ffmpeg_opt.c methods called by both ffmpeg and ffprobe added) * by multiple threads, signatures of ffmpeg_opt.c methods called by both ffmpeg
* and ffprobe added)
* *
* 03.2019 * 03.2019
* -------------------------------------------------------- * --------------------------------------------------------
@ -70,10 +77,10 @@
#include "config.h" #include "config.h"
#include <signal.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h>
#include "fftools_cmdutils.h" #include "fftools_cmdutils.h"
#include "fftools_sync_queue.h" #include "fftools_sync_queue.h"
@ -103,6 +110,11 @@
#define FFMPEG_OPT_MAP_CHANNEL 1 #define FFMPEG_OPT_MAP_CHANNEL 1
#define FFMPEG_OPT_MAP_SYNC 1 #define FFMPEG_OPT_MAP_SYNC 1
#define FFMPEG_ROTATION_METADATA 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 { enum VideoSyncMethod {
VSYNC_AUTO = -1, VSYNC_AUTO = -1,
@ -113,7 +125,10 @@ enum VideoSyncMethod {
VSYNC_DROP, VSYNC_DROP,
}; };
#define MAX_STREAMS 1024 /* arbitrary sanity check value */ enum EncTimeBase {
ENC_TIME_BASE_DEMUX = -1,
ENC_TIME_BASE_FILTER = -2,
};
enum HWAccelID { enum HWAccelID {
HWACCEL_NONE = 0, HWACCEL_NONE = 0,
@ -142,6 +157,12 @@ typedef struct {
} AudioChannelMap; } AudioChannelMap;
#endif #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 { typedef struct OptionsContext {
OptionGroup *g; OptionGroup *g;
@ -173,6 +194,7 @@ typedef struct OptionsContext {
int loop; int loop;
int rate_emu; int rate_emu;
float readrate; float readrate;
double readrate_initial_burst;
int accurate_seek; int accurate_seek;
int thread_queue_size; int thread_queue_size;
int input_sync_ref; int input_sync_ref;
@ -217,9 +239,8 @@ typedef struct OptionsContext {
int subtitle_disable; int subtitle_disable;
int data_disable; int data_disable;
/* indexed by output file stream index */ // keys are stream indices
int *streamid_map; AVDictionary *streamid;
int nb_streamid_map;
SpecifierOpt *metadata; SpecifierOpt *metadata;
int nb_metadata; int nb_metadata;
@ -255,8 +276,10 @@ typedef struct OptionsContext {
int nb_inter_matrices; int nb_inter_matrices;
SpecifierOpt *chroma_intra_matrices; SpecifierOpt *chroma_intra_matrices;
int nb_chroma_intra_matrices; int nb_chroma_intra_matrices;
#if FFMPEG_OPT_TOP
SpecifierOpt *top_field_first; SpecifierOpt *top_field_first;
int nb_top_field_first; int nb_top_field_first;
#endif
SpecifierOpt *metadata_map; SpecifierOpt *metadata_map;
int nb_metadata_map; int nb_metadata_map;
SpecifierOpt *presets; SpecifierOpt *presets;
@ -318,62 +341,33 @@ typedef struct OptionsContext {
} OptionsContext; } OptionsContext;
typedef struct InputFilter { typedef struct InputFilter {
AVFilterContext *filter;
struct InputStream *ist;
struct FilterGraph *graph; struct FilterGraph *graph;
uint8_t *name; 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; } InputFilter;
typedef struct OutputFilter { typedef struct OutputFilter {
AVFilterContext *filter;
struct OutputStream *ost; struct OutputStream *ost;
struct FilterGraph *graph; struct FilterGraph *graph;
uint8_t *name; uint8_t *name;
/* temporary storage until stream maps are processed */ /* for filters that are not yet bound to an output stream,
AVFilterInOut *out_tmp; * this stores the output linklabel, if any */
uint8_t *linklabel;
enum AVMediaType type; enum AVMediaType type;
/* desired output stream properties */ /* pts of the last frame received from this filter, in AV_TIME_BASE_Q */
int width, height; int64_t last_pts;
AVRational frame_rate;
int format; uint64_t nb_frames_dup;
int sample_rate; uint64_t nb_frames_drop;
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;
} OutputFilter; } OutputFilter;
typedef struct FilterGraph { typedef struct FilterGraph {
const AVClass *clazz;
int index; int index;
const char *graph_desc;
AVFilterGraph *graph; AVFilterGraph *graph;
int reconfiguration;
// true when the filtergraph contains only meta filters
// that do not modify the frame data
int is_meta;
InputFilter **inputs; InputFilter **inputs;
int nb_inputs; int nb_inputs;
@ -381,17 +375,21 @@ typedef struct FilterGraph {
int nb_outputs; int nb_outputs;
} FilterGraph; } FilterGraph;
typedef struct Decoder Decoder;
typedef struct InputStream { typedef struct InputStream {
const AVClass *clazz;
int file_index; int file_index;
int index;
AVStream *st; AVStream *st;
int discard; /* true if stream data should be discarded */ int discard; /* true if stream data should be discarded */
int user_set_discard; 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_OST 1
#define DECODING_FOR_FILTER 2 #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. * Codec parameters - to be used by the decoding/streamcopy code.
@ -399,65 +397,28 @@ typedef struct InputStream {
* concurrently by the demuxing thread. * concurrently by the demuxing thread.
*/ */
AVCodecParameters *par; AVCodecParameters *par;
Decoder *decoder;
AVCodecContext *dec_ctx; AVCodecContext *dec_ctx;
const AVCodec *dec; const AVCodec *dec;
AVFrame *decoded_frame; const AVCodecDescriptor *codec_desc;
AVPacket *pkt;
AVRational framerate_guessed; AVRational framerate_guessed;
int64_t prev_pkt_pts; int64_t nb_samples; /* number of samples in the last decoded audio frame
int64_t start; /* time when read started */ before looping */
/* 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 */
double ts_scale;
int saw_first_ts;
AVDictionary *decoder_opts; AVDictionary *decoder_opts;
AVRational framerate; /* framerate forced with -r */ AVRational framerate; /* framerate forced with -r */
#if FFMPEG_OPT_TOP
int top_field_first; int top_field_first;
int guess_layout_max; #endif
int autorotate; int autorotate;
int fix_sub_duration; int fix_sub_duration;
struct { /* previous decoded subtitle and related variables */
int got_output;
int ret;
AVSubtitle subtitle;
} prev_sub;
struct sub2video { struct sub2video {
int64_t last_pts;
int64_t end_pts;
AVFifo *sub_queue; ///< queue of AVSubtitle* before filter init
AVFrame *frame;
int w, h; int w, h;
unsigned int initialize; ///< marks if sub2video_update should force an initialization
} sub2video; } sub2video;
/* decoded data from this stream goes into all those filters /* decoded data from this stream goes into all those filters
@ -465,6 +426,14 @@ typedef struct InputStream {
InputFilter **filters; InputFilter **filters;
int nb_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; int reinit_filters;
/* hwaccel options */ /* hwaccel options */
@ -473,22 +442,11 @@ typedef struct InputStream {
char *hwaccel_device; char *hwaccel_device;
enum AVPixelFormat hwaccel_output_format; enum AVPixelFormat hwaccel_output_format;
int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame);
enum AVPixelFormat hwaccel_pix_fmt;
/* stats */ /* 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 // number of frames/samples retrieved from the decoder
uint64_t frames_decoded; uint64_t frames_decoded;
uint64_t samples_decoded; uint64_t samples_decoded;
uint64_t decode_errors;
int64_t *dts_buffer;
int nb_dts_buffer;
int got_output;
} InputStream; } InputStream;
typedef struct LastFrameDuration { typedef struct LastFrameDuration {
@ -497,8 +455,13 @@ typedef struct LastFrameDuration {
} LastFrameDuration; } LastFrameDuration;
typedef struct InputFile { typedef struct InputFile {
const AVClass *clazz;
int index; int index;
// input format has no timestamps
int format_nots;
AVFormatContext *ctx; AVFormatContext *ctx;
int eof_reached; /* true if eof reached */ int eof_reached; /* true if eof reached */
int eagain; /* true if last read attempt returned EAGAIN */ int eagain; /* true if last read attempt returned EAGAIN */
@ -509,12 +472,8 @@ typedef struct InputFile {
*/ */
int64_t start_time_effective; int64_t start_time_effective;
int64_t ts_offset; int64_t ts_offset;
/** int64_t start_time; /* user-specified start time in AV_TIME_BASE or
* Extra timestamp offset added by discontinuity handling. AV_NOPTS_VALUE */
*/
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 recording_time; int64_t recording_time;
/* streams that ffmpeg is aware of; /* streams that ffmpeg is aware of;
@ -523,7 +482,6 @@ typedef struct InputFile {
InputStream **streams; InputStream **streams;
int nb_streams; int nb_streams;
int rate_emu;
float readrate; float readrate;
int accurate_seek; int accurate_seek;
@ -585,11 +543,13 @@ extern const char *const forced_keyframes_const_names[];
typedef enum { typedef enum {
ENCODER_FINISHED = 1, ENCODER_FINISHED = 1,
MUXER_FINISHED = 2, MUXER_FINISHED = 2,
} OSTFinished ; } OSTFinished;
enum { enum {
KF_FORCE_SOURCE = 1, KF_FORCE_SOURCE = 1,
#if FFMPEG_OPT_FORCE_KF_SOURCE_NO_DROP
KF_FORCE_SOURCE_NO_DROP = 2, KF_FORCE_SOURCE_NO_DROP = 2,
#endif
}; };
typedef struct KeyframeForceCtx { typedef struct KeyframeForceCtx {
@ -608,44 +568,35 @@ typedef struct KeyframeForceCtx {
int dropped_keyframe; int dropped_keyframe;
} KeyframeForceCtx; } KeyframeForceCtx;
typedef struct Encoder Encoder;
typedef struct OutputStream { typedef struct OutputStream {
const AVClass *clazz; const AVClass *clazz;
enum AVMediaType type;
int file_index; /* file index */ int file_index; /* file index */
int index; /* stream index in the output file */ 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; /* input stream that is the source for this output stream;
* may be NULL for streams with no well-defined source, e.g. * may be NULL for streams with no well-defined source, e.g.
* attachments or outputs from complex filtergraphs */ * attachments or outputs from complex filtergraphs */
InputStream *ist; InputStream *ist;
AVStream *st; /* stream in the output file */ 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 */ /* dts of the last packet sent to the muxing queue, in AV_TIME_BASE_Q */
int64_t last_mux_dts; 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; AVRational enc_timebase;
Encoder *enc;
AVCodecContext *enc_ctx; 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 */ /* video only */
AVRational frame_rate; AVRational frame_rate;
@ -653,7 +604,9 @@ typedef struct OutputStream {
enum VideoSyncMethod vsync_method; enum VideoSyncMethod vsync_method;
int is_cfr; int is_cfr;
int force_fps; int force_fps;
#if FFMPEG_OPT_TOP
int top_field_first; int top_field_first;
#endif
#if FFMPEG_ROTATION_METADATA #if FFMPEG_ROTATION_METADATA
int rotate_overridden; int rotate_overridden;
#endif #endif
@ -670,7 +623,8 @@ typedef struct OutputStream {
/* audio only */ /* audio only */
#if FFMPEG_OPT_MAP_CHANNEL #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 */ int audio_channels_mapped; /* number of channels in audio_channels_map */
#endif #endif
@ -678,53 +632,35 @@ typedef struct OutputStream {
FILE *logfile; FILE *logfile;
OutputFilter *filter; 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 *encoder_opts;
AVDictionary *sws_dict; AVDictionary *sws_dict;
AVDictionary *swr_opts; AVDictionary *swr_opts;
char *apad; char *apad;
OSTFinished finished; /* no more packets should be written for this stream */ OSTFinished
int unavailable; /* true if the steram is unavailable (possibly temporarily) */ 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 // init_output_stream() has been called for this stream
// The encoder and the bitstream filters have been initialized and the stream // The encoder and the bitstream filters have been initialized and the
// parameters are set in the AVStream. // stream parameters are set in the AVStream.
int initialized; int initialized;
int inputs_done;
const char *attachment_filename; const char *attachment_filename;
int streamcopy_started;
int copy_initial_nonkeyframes;
int copy_prior_start;
int keep_pix_fmt; int keep_pix_fmt;
/* stats */ /* 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 // number of packets send to the muxer
atomic_uint_least64_t packets_written; atomic_uint_least64_t packets_written;
// number of frames/samples sent to the encoder // number of frames/samples sent to the encoder
uint64_t frames_encoded; uint64_t frames_encoded;
uint64_t samples_encoded; uint64_t samples_encoded;
// number of packets received from the encoder
uint64_t packets_encoded;
/* packet quality factor */ /* packet quality factor */
int quality; 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_encode;
int sq_idx_mux; int sq_idx_mux;
@ -751,13 +687,29 @@ typedef struct OutputFile {
SyncQueue *sq_encode; 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 int64_t start_time; ///< start time in microseconds == AV_TIME_BASE units
int shortest; int shortest;
int bitexact; int bitexact;
} OutputFile; } 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 InputFile **input_files;
extern __thread int nb_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 *vstats_filename;
extern __thread char *sdp_filename; extern __thread char *sdp_filename;
extern __thread float audio_drift_threshold;
extern __thread float dts_delta_threshold; extern __thread float dts_delta_threshold;
extern __thread float dts_error_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 abort_on_flags;
extern __thread int print_stats; extern __thread int print_stats;
extern __thread int64_t stats_period; extern __thread int64_t stats_period;
extern __thread int qp_hist;
extern __thread int stdin_interaction; extern __thread int stdin_interaction;
extern __thread AVIOContext *progress_avio; extern __thread AVIOContext *progress_avio;
extern __thread float max_error_rate; extern __thread float max_error_rate;
@ -810,6 +760,8 @@ extern __thread int copy_unknown_streams;
extern __thread int recast_media; extern __thread int recast_media;
extern __thread FILE *vstats_file;
#if FFMPEG_OPT_PSNR #if FFMPEG_OPT_PSNR
extern __thread int do_psnr; extern __thread int do_psnr;
#endif #endif
@ -820,40 +772,120 @@ void term_exit(void);
void show_usage(void); void show_usage(void);
void remove_avoptions(AVDictionary **a, AVDictionary *b); 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); char *file_read(const char *filename);
AVDictionary *strip_specifiers(const AVDictionary *dict); AVDictionary *strip_specifiers(const AVDictionary *dict);
const AVCodec *find_codec_or_die(void *logctx, const char *name, int find_codec(void *logctx, const char *name, enum AVMediaType type,
enum AVMediaType type, int encoder); 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 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 check_filter_outputs(void);
int filtergraph_is_simple(FilterGraph *fg); int filtergraph_is_simple(const FilterGraph *fg);
int init_simple_filtergraph(InputStream *ist, OutputStream *ost); int init_simple_filtergraph(InputStream *ist, OutputStream *ost,
char *graph_desc);
int init_complex_filtergraph(FilterGraph *fg); 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);
int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame); /**
* Get our axiliary frame data attached to the frame, allocating it
* if needed.
*/
FrameData *frame_data(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); int ffmpeg_parse_options(int argc, char **argv);
void enc_stats_write(OutputStream *ost, EncStats *es, void enc_stats_write(OutputStream *ost, EncStats *es, const AVFrame *frame,
const AVFrame *frame, const AVPacket *pkt, const AVPacket *pkt, uint64_t frame_num);
uint64_t frame_num);
HWDevice *hw_device_get_by_name(const char *name); 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_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); void hw_device_free_all(void);
int hw_device_setup_for_decode(InputStream *ist); /**
int hw_device_setup_for_encode(OutputStream *ost); * Get a hardware device to be used with this filtergraph.
int hw_device_setup_for_filter(FilterGraph *fg); * 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_decode_init(AVCodecContext *avctx); int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input);
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 * 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_stream_init(OutputFile *of, OutputStream *ost);
int of_write_trailer(OutputFile *of); int of_write_trailer(OutputFile *of);
int of_open(const OptionsContext *o, const char *filename); 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); void of_enc_stats_close(void);
/* int of_output_packet(OutputFile *of, OutputStream *ost, AVPacket *pkt);
* 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 * @param dts predicted packet dts in AV_TIME_BASE_Q
* 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.
*/ */
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); int64_t of_filesize(OutputFile *of);
int ifile_open(const OptionsContext *o, const char *filename); 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 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; /* iterate over all input streams in all input files;
* pass NULL to start iteration */ * pass NULL to start iteration */
InputStream *ist_iter(InputStream *prev); InputStream *ist_iter(InputStream *prev);
extern const char * const opt_name_codec_names[]; /* iterate over all output streams in all output files;
extern const char * const opt_name_codec_tags[]; * pass NULL to start iteration */
extern const char * const opt_name_frame_rates[]; OutputStream *ost_iter(OutputStream *prev);
extern const char * const opt_name_top_field_first[];
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); void cancel_operation(long id);
#endif /* FFTOOLS_FFMPEG_H */ #endif /* FFTOOLS_FFMPEG_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

Loading…
Cancel
Save