diff --git a/android/app/src/main/cpp/ffmpegkit.c b/android/app/src/main/cpp/ffmpegkit.c index 240b283..991a91b 100644 --- a/android/app/src/main/cpp/ffmpegkit.c +++ b/android/app/src/main/cpp/ffmpegkit.c @@ -50,6 +50,7 @@ struct CallbackData { /** Session map variables */ const int SESSION_MAP_SIZE = 1000; static volatile int sessionMap[SESSION_MAP_SIZE]; +static volatile int sessionInTransitMessageCountMap[SESSION_MAP_SIZE]; static pthread_mutex_t sessionMapMutex; /** Redirection control variables */ @@ -113,11 +114,12 @@ JNINativeMethod configMethods[] = { {"getNativeVersion", "()Ljava/lang/String;", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_getNativeVersion}, {"nativeFFmpegExecute", "(J[Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegExecute}, {"nativeFFmpegCancel", "(J)V", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpegCancel}, - {"nativeFFprobeExecute", "([Ljava/lang/String;)I", (void*) Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprobeExecute}, + {"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} + {"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 */ @@ -213,7 +215,7 @@ void monitorInit() { pthread_condattr_destroy(&cattributes); } -void executionMapLockInit() { +void sessionMapLockInit() { pthread_mutexattr_t attributes; pthread_mutexattr_init(&attributes); pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE_NP); @@ -231,7 +233,7 @@ void monitorUnInit() { pthread_cond_destroy(&monitorCondition); } -void executionMapLockUnInit() { +void sessionMapLockUnInit() { pthread_mutex_destroy(&sessionMapMutex); } @@ -314,6 +316,9 @@ void logCallbackDataAdd(int level, AVBPrint *data) { callbackDataTail = newData; } + int key = sessionId % SESSION_MAP_SIZE; + sessionInTransitMessageCountMap[key] += 1; + mutexUnlock(); monitorNotify(); @@ -356,6 +361,9 @@ void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_ callbackDataTail = newData; } + int key = sessionId % SESSION_MAP_SIZE; + sessionInTransitMessageCountMap[key] += 1; + mutexUnlock(); monitorNotify(); @@ -456,6 +464,20 @@ int cancelRequested(long id) { return found; } +/** + * Resets the number of messages in transmit for this session. + * + * @param id session id + */ +void resetMessagesInTransmit(long id) { + mutexLock(); + + int key = id % SESSION_MAP_SIZE; + sessionInTransitMessageCountMap[key] = 0; + + mutexUnlock(); +} + /** * Callback function for FFmpeg logs. * @@ -566,6 +588,9 @@ void *callbackThreadFunction() { } + int key = callbackData->sessionId % SESSION_MAP_SIZE; + sessionInTransitMessageCountMap[key] -= 1; + // CLEAN STRUCT callbackData->next = NULL; av_free(callbackData); @@ -611,7 +636,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_FALSE; } - if ((*env)->RegisterNatives(env, localConfigClass, configMethods, 13) < 0) { + if ((*env)->RegisterNatives(env, localConfigClass, configMethods, 14) < 0) { LOGE("OnLoad failed to RegisterNatives for class %s.\n", configClassName); return JNI_FALSE; } @@ -664,7 +689,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { mutexInit(); monitorInit(); - executionMapLockInit(); + sessionMapLockInit(); return JNI_VERSION_1_6; } @@ -805,10 +830,12 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFmpeg } } - // REGISTER THE ID BEFORE STARTING EXECUTION + // REGISTER THE ID BEFORE STARTING THE EXECUTION sessionId = (long) id; addSession((long) id); + resetMessagesInTransmit(sessionId); + // RUN int retCode = ffmpeg_execute(argumentCount, argv); @@ -907,3 +934,22 @@ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNative handleSIGPIPE = 0; } } + +/** + * Returns the number of native messages which are not transmitted to the Java callbacks for the + * given session. + * + * @param env pointer to native method interface + * @param object reference to the class on which this method is invoked + * @param id session id + */ +JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit(JNIEnv *env, jclass object, jlong id) { + mutexLock(); + + int key = id % SESSION_MAP_SIZE; + int count = sessionInTransitMessageCountMap[key]; + + mutexUnlock(); + + return count; +} diff --git a/android/app/src/main/cpp/ffmpegkit.h b/android/app/src/main/cpp/ffmpegkit.h index 6a357fd..5f82a1b 100644 --- a/android/app/src/main/cpp/ffmpegkit.h +++ b/android/app/src/main/cpp/ffmpegkit.h @@ -131,4 +131,11 @@ JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_setNativeEnvi */ JNIEXPORT void JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_ignoreNativeSignal(JNIEnv *env, jclass object, jint signum); +/* + * Class: com_arthenica_ffmpegkit_FFmpegKitConfig + * Method: messagesInTransmit + * Signature: (J)I + */ +JNIEXPORT int JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_messagesInTransmit(JNIEnv *env, jclass object, jlong id); + #endif /* FFMPEG_KIT_H */ \ No newline at end of file diff --git a/android/app/src/main/cpp/ffprobekit.c b/android/app/src/main/cpp/ffprobekit.c index 027f148..5097a6c 100644 --- a/android/app/src/main/cpp/ffprobekit.c +++ b/android/app/src/main/cpp/ffprobekit.c @@ -33,6 +33,7 @@ extern int configuredLogLevel; extern __thread volatile long sessionId; extern void addSession(long id); extern void removeSession(long id); +extern void resetMessagesInTransmit(long id); /** * Synchronously executes FFprobe natively with arguments provided. @@ -76,10 +77,12 @@ JNIEXPORT jint JNICALL Java_com_arthenica_ffmpegkit_FFmpegKitConfig_nativeFFprob } } - // REGISTER THE ID BEFORE STARTING EXECUTION + // REGISTER THE ID BEFORE STARTING THE EXECUTION sessionId = (long) id; addSession((long) id); + resetMessagesInTransmit(sessionId); + // RUN int retCode = ffprobe_execute(argumentCount, argv); diff --git a/android/app/src/main/cpp/fftools_ffmpeg.c b/android/app/src/main/cpp/fftools_ffmpeg.c index 9fe0e29..0840f9d 100644 --- a/android/app/src/main/cpp/fftools_ffmpeg.c +++ b/android/app/src/main/cpp/fftools_ffmpeg.c @@ -1846,12 +1846,6 @@ static void print_report(int is_last_report, int64_t timer_start, int64_t cur_ti int ret; float t; - // FORWARD IT BEFORE PROCESSING - forward_report(is_last_report, timer_start, cur_time); - - if (!print_stats && !is_last_report && !progress_avio) - return; - if (!is_last_report) { if (last_time == -1) { last_time = cur_time; @@ -1862,6 +1856,11 @@ static void print_report(int is_last_report, int64_t timer_start, int64_t cur_ti last_time = cur_time; } + forward_report(is_last_report, timer_start, cur_time); + + if (!print_stats && !is_last_report && !progress_avio) + return; + t = (cur_time-timer_start) / 1000000.0; diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/AbstractSession.java b/android/app/src/main/java/com/arthenica/ffmpegkit/AbstractSession.java index 62750b8..3c094f6 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/AbstractSession.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/AbstractSession.java @@ -22,22 +22,22 @@ package com.arthenica.ffmpegkit; import com.arthenica.smartexception.java.Exceptions; import java.util.Date; -import java.util.Optional; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.BinaryOperator; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; public abstract class AbstractSession implements Session { /** * Generates ids for execute sessions. */ - private static final AtomicLong sessionIdGenerator = new AtomicLong(1); + protected static final AtomicLong sessionIdGenerator = new AtomicLong(1); + + /** + * Defines how long default `getAll` methods wait. + */ + protected static final int DEFAULT_TIMEOUT_FOR_CALLBACK_MESSAGES_IN_TRANSMIT = 5000; protected final ExecuteCallback executeCallback; protected final LogCallback logCallback; @@ -52,11 +52,13 @@ public abstract class AbstractSession implements Session { protected SessionState state; protected int returnCode; protected String failStackTrace; + protected final LogRedirectionStrategy logRedirectionStrategy; public AbstractSession(final String[] arguments, final ExecuteCallback executeCallback, final LogCallback logCallback, - final StatisticsCallback statisticsCallback) { + final StatisticsCallback statisticsCallback, + final LogRedirectionStrategy logRedirectionStrategy) { this.sessionId = sessionIdGenerator.getAndIncrement(); this.createTime = new Date(); this.startTime = null; @@ -69,36 +71,45 @@ public abstract class AbstractSession implements Session { this.state = SessionState.CREATED; this.returnCode = ReturnCode.NOT_SET; this.failStackTrace = null; + this.logRedirectionStrategy = logRedirectionStrategy; } + @Override public ExecuteCallback getExecuteCallback() { return executeCallback; } + @Override public LogCallback getLogCallback() { return logCallback; } + @Override public StatisticsCallback getStatisticsCallback() { return statisticsCallback; } + @Override public long getSessionId() { return sessionId; } + @Override public Date getCreateTime() { return createTime; } + @Override public Date getStartTime() { return startTime; } + @Override public Date getEndTime() { return endTime; } + @Override public long getDuration() { final Date startTime = this.startTime; final Date endTime = this.endTime; @@ -109,85 +120,141 @@ public abstract class AbstractSession implements Session { return -1; } + @Override public String[] getArguments() { return arguments; } + @Override public String getCommand() { return FFmpegKit.argumentsToString(arguments); } + protected void waitForCallbackMessagesInTransmit(final int timeout) { + final long start = System.currentTimeMillis(); + + /* + * WE GIVE MAX 5 SECONDS TO TRANSMIT ALL NATIVE MESSAGES + */ + while (thereAreCallbackMessagesInTransmit() && (System.currentTimeMillis() < (start + timeout))) { + synchronized (this) { + try { + wait(100); + } catch (InterruptedException ignored) { + } + } + } + } + + @Override + public Queue getAllLogs(final int waitTimeout) { + waitForCallbackMessagesInTransmit(waitTimeout); + + if (thereAreCallbackMessagesInTransmit()) { + android.util.Log.i(FFmpegKitConfig.TAG, String.format("getAllLogs was asked to return all logs but there are still logs being transmitted for session id %d.", sessionId)); + } + + return logs; + } + + @Override + public Queue getAllLogs() { + return getAllLogs(DEFAULT_TIMEOUT_FOR_CALLBACK_MESSAGES_IN_TRANSMIT); + } + + @Override public Queue getLogs() { return logs; } - public Stream getLogsAsStream() { - return logs.stream(); + @Override + public String getAllLogsAsString(final int waitTimeout) { + waitForCallbackMessagesInTransmit(waitTimeout); + + if (thereAreCallbackMessagesInTransmit()) { + android.util.Log.i(FFmpegKitConfig.TAG, String.format("getAllLogsAsString was asked to return all logs but there are still logs being transmitted for session id %d.", sessionId)); + } + + return getLogsAsString(); } + @Override + public String getAllLogsAsString() { + return getAllLogsAsString(DEFAULT_TIMEOUT_FOR_CALLBACK_MESSAGES_IN_TRANSMIT); + } + + @Override public String getLogsAsString() { - final Optional concatenatedStringOption = logs.stream().map(new Function() { - @Override - public String apply(final Log log) { - return log.getMessage(); - } - }).reduce(new BinaryOperator() { - @Override - public String apply(final String s1, final String s2) { - return s1 + s2; - } - }); + final StringBuilder concatenatedString = new StringBuilder(); - return concatenatedStringOption.orElseGet(new Supplier() { + for (Log log : logs) { + concatenatedString.append(log.getMessage()); + } - @Override - public String get() { - return ""; - } - }); + return concatenatedString.toString(); } + @Override public SessionState getState() { return state; } + @Override public int getReturnCode() { return returnCode; } + @Override public String getFailStackTrace() { return failStackTrace; } + @Override + public LogRedirectionStrategy getLogRedirectionStrategy() { + return logRedirectionStrategy; + } + + @Override + public boolean thereAreCallbackMessagesInTransmit() { + return (FFmpegKitConfig.messagesInTransmit(sessionId) != 0); + } + + @Override public void addLog(final Log log) { this.logs.add(log); } + @Override public Future getFuture() { return future; } + @Override public void setFuture(final Future future) { this.future = future; } + @Override public void startRunning() { this.state = SessionState.RUNNING; this.startTime = new Date(); } + @Override public void complete(final int returnCode) { this.returnCode = returnCode; this.state = SessionState.COMPLETED; this.endTime = new Date(); } + @Override public void fail(final Exception exception) { this.failStackTrace = Exceptions.getStackTraceString(exception); this.state = SessionState.FAILED; this.endTime = new Date(); } + @Override public void cancel() { if (state == SessionState.RUNNING) { FFmpegKit.cancel(sessionId); diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/AsyncFFprobeExecuteTask.java b/android/app/src/main/java/com/arthenica/ffmpegkit/AsyncFFprobeExecuteTask.java index 654d563..ebe7c26 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/AsyncFFprobeExecuteTask.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/AsyncFFprobeExecuteTask.java @@ -35,6 +35,11 @@ public class AsyncFFprobeExecuteTask implements Runnable { public void run() { FFmpegKitConfig.ffprobeExecute(ffprobeSession); + final ExecuteCallback globalExecuteCallbackFunction = FFmpegKitConfig.getGlobalExecuteCallbackFunction(); + if (globalExecuteCallbackFunction != null) { + globalExecuteCallbackFunction.apply(ffprobeSession); + } + if (executeCallback != null) { executeCallback.apply(ffprobeSession); } diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/AsyncGetMediaInformationTask.java b/android/app/src/main/java/com/arthenica/ffmpegkit/AsyncGetMediaInformationTask.java index 8b08350..5c78a94 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/AsyncGetMediaInformationTask.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/AsyncGetMediaInformationTask.java @@ -25,15 +25,26 @@ package com.arthenica.ffmpegkit; public class AsyncGetMediaInformationTask implements Runnable { private final MediaInformationSession mediaInformationSession; private final ExecuteCallback executeCallback; + private final Integer waitTimeout; public AsyncGetMediaInformationTask(final MediaInformationSession mediaInformationSession) { + this(mediaInformationSession, AbstractSession.DEFAULT_TIMEOUT_FOR_CALLBACK_MESSAGES_IN_TRANSMIT); + } + + public AsyncGetMediaInformationTask(final MediaInformationSession mediaInformationSession, final Integer waitTimeout) { this.mediaInformationSession = mediaInformationSession; this.executeCallback = mediaInformationSession.getExecuteCallback(); + this.waitTimeout = waitTimeout; } @Override public void run() { - FFmpegKitConfig.getMediaInformationExecute(mediaInformationSession); + FFmpegKitConfig.getMediaInformationExecute(mediaInformationSession, waitTimeout); + + final ExecuteCallback globalExecuteCallbackFunction = FFmpegKitConfig.getGlobalExecuteCallbackFunction(); + if (globalExecuteCallbackFunction != null) { + globalExecuteCallbackFunction.apply(mediaInformationSession); + } if (executeCallback != null) { executeCallback.apply(mediaInformationSession); diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegKit.java b/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegKit.java index 9ccfc25..391df84 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegKit.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegKit.java @@ -27,12 +27,12 @@ import java.util.concurrent.Executor; *

Main class for FFmpeg operations. Supports synchronous {@link #execute(String...)} and * asynchronous {@link #executeAsync(String, ExecuteCallback)} methods to execute FFmpeg commands. *

- *      int rc = FFmpeg.execute("-i file1.mp4 -c:v libxvid file1.avi");
- *      Log.i(Config.TAG, String.format("Command execution %s.", (rc == 0?"completed successfully":"failed with rc=" + rc));
+ *      FFmpegSession session = FFmpeg.execute("-i file1.mp4 -c:v libxvid file1.avi");
+ *      Log.i(Config.TAG, String.format("Command execution completed with %d.", session.getReturnCode());
  * 
*
- *      long executionId = FFmpeg.executeAsync("-i file1.mp4 -c:v libxvid file1.avi", executeCallback);
- *      Log.i(Config.TAG, String.format("Asynchronous execution %d started.", executionId));
+ *      FFmpegSession session = FFmpeg.executeAsync("-i file1.mp4 -c:v libxvid file1.avi", executeCallback);
+ *      Log.i(Config.TAG, String.format("Asynchronous session %d started.", session.getSessionId()));
  * 
*/ public class FFmpegKit { @@ -231,17 +231,18 @@ public class FFmpegKit { } /** - *

Cancels the last execution started. + *

Cancels all ongoing executions. * *

This function does not wait for termination to complete and returns immediately. */ public static void cancel() { - Session lastSession = FFmpegKitConfig.getLastSession(); - if (lastSession != null) { - FFmpegKitConfig.nativeFFmpegCancel(lastSession.getSessionId()); - } else { - android.util.Log.w(FFmpegKitConfig.TAG, "FFmpegKit cancel skipped. The last execution does not exist."); - } + + /* + * ZERO (0) IS A SPECIAL SESSION ID + * WHEN IT IS PASSED TO THIS METHOD, A SIGINT IS GENERATED WHICH CANCELS ALL ONGOING + * EXECUTIONS + */ + FFmpegKitConfig.nativeFFmpegCancel(0); } /** @@ -249,7 +250,7 @@ public class FFmpegKit { * *

This function does not wait for termination to complete and returns immediately. * - * @param sessionId id of the session that will be stopped + * @param sessionId id of the session that will be cancelled */ public static void cancel(final long sessionId) { FFmpegKitConfig.nativeFFmpegCancel(sessionId); diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java b/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java index 17f0eef..0d2fb5a 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java @@ -45,10 +45,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collectors; /** *

This class is used to configure FFmpegKit library and tools coming with it. @@ -85,11 +81,12 @@ public class FFmpegKitConfig { * Generates ids for named ffmpeg kit pipes. */ private static final AtomicLong pipeIndexGenerator; + + /* SESSION HISTORY VARIABLES */ + private static int sessionHistorySize; private static final Map sessionHistoryMap; private static final Queue sessionHistoryQueue; private static final Object sessionHistoryLock; - private static int asyncConcurrencyLimit; - private static final SparseArray pfdMap; /** * Executor service for async executions. @@ -99,9 +96,9 @@ public class FFmpegKitConfig { private static StatisticsCallback globalStatisticsCallbackFunction; private static ExecuteCallback globalExecuteCallbackFunction; private static Level activeLogLevel; - - /* SESSION HISTORY VARIABLES */ - private static int sessionHistorySize; + private static int asyncConcurrencyLimit; + private static final SparseArray pfdMap; + private static LogRedirectionStrategy logRedirectionStrategy; static { @@ -192,6 +189,7 @@ public class FFmpegKitConfig { sessionHistoryLock = new Object(); pfdMap = new SparseArray<>(); + logRedirectionStrategy = LogRedirectionStrategy.PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED; enableRedirection(); } @@ -236,6 +234,9 @@ public class FFmpegKitConfig { final Level level = Level.from(levelValue); final String text = new String(logMessage); final Log log = new Log(sessionId, level, text); + boolean globalCallbackDefined = false; + boolean sessionCallbackDefined = false; + LogRedirectionStrategy activeLogRedirectionStrategy = FFmpegKitConfig.logRedirectionStrategy; // AV_LOG_STDERR logs are always redirected if ((activeLogLevel == Level.AV_LOG_QUIET && levelValue != Level.AV_LOG_STDERR.getValue()) || levelValue > activeLogLevel.getValue()) { @@ -244,58 +245,88 @@ public class FFmpegKitConfig { } final Session session = getSession(sessionId); - if (session != null && session.getLogCallback() != null) { - try { - // NOTIFY SESSION CALLBACK IF DEFINED - session.getLogCallback().apply(log); - } catch (final Exception e) { - android.util.Log.e(FFmpegKitConfig.TAG, String.format("Exception thrown inside session LogCallback block.%s", Exceptions.getStackTraceString(e))); + if (session != null) { + activeLogRedirectionStrategy = session.getLogRedirectionStrategy(); + session.addLog(log); + + if (session.getLogCallback() != null) { + sessionCallbackDefined = true; + + try { + // NOTIFY SESSION CALLBACK IF DEFINED + session.getLogCallback().apply(log); + } catch (final Exception e) { + android.util.Log.e(FFmpegKitConfig.TAG, String.format("Exception thrown inside session LogCallback block.%s", Exceptions.getStackTraceString(e))); + } } } final LogCallback globalLogCallbackFunction = FFmpegKitConfig.globalLogCallbackFunction; if (globalLogCallbackFunction != null) { + globalCallbackDefined = true; + try { // NOTIFY GLOBAL CALLBACK IF DEFINED globalLogCallbackFunction.apply(log); } catch (final Exception e) { android.util.Log.e(FFmpegKitConfig.TAG, String.format("Exception thrown inside global LogCallback block.%s", Exceptions.getStackTraceString(e))); } - } else { - switch (level) { - case AV_LOG_QUIET: { - // PRINT NO OUTPUT - } - break; - case AV_LOG_TRACE: - case AV_LOG_DEBUG: { - android.util.Log.d(TAG, text); - } - break; - case AV_LOG_STDERR: - case AV_LOG_VERBOSE: { - android.util.Log.v(TAG, text); - } - break; - case AV_LOG_INFO: { - android.util.Log.i(TAG, text); - } - break; - case AV_LOG_WARNING: { - android.util.Log.w(TAG, text); - } - break; - case AV_LOG_ERROR: - case AV_LOG_FATAL: - case AV_LOG_PANIC: { - android.util.Log.e(TAG, text); - } - break; - default: { - android.util.Log.v(TAG, text); - } - break; + } + + // EXECUTE THE LOG STRATEGY + switch (activeLogRedirectionStrategy) { + case NEVER_PRINT_LOGS: { + return; } + case PRINT_LOGS_WHEN_GLOBAL_CALLBACK_NOT_DEFINED: { + if (globalCallbackDefined) { + return; + } + } + break; + case PRINT_LOGS_WHEN_SESSION_CALLBACK_NOT_DEFINED: { + if (sessionCallbackDefined) { + return; + } + } + case PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED: { + if (globalCallbackDefined || sessionCallbackDefined) { + return; + } + } + } + + // PRINT LOGS + switch (level) { + case AV_LOG_QUIET: { + // PRINT NO OUTPUT + } + break; + case AV_LOG_TRACE: + case AV_LOG_DEBUG: { + android.util.Log.d(TAG, text); + } + break; + case AV_LOG_INFO: { + android.util.Log.i(TAG, text); + } + break; + case AV_LOG_WARNING: { + android.util.Log.w(TAG, text); + } + break; + case AV_LOG_ERROR: + case AV_LOG_FATAL: + case AV_LOG_PANIC: { + android.util.Log.e(TAG, text); + } + break; + case AV_LOG_STDERR: + case AV_LOG_VERBOSE: + default: { + android.util.Log.v(TAG, text); + } + break; } } @@ -314,15 +345,19 @@ public class FFmpegKitConfig { private static void statistics(final long sessionId, final int videoFrameNumber, final float videoFps, final float videoQuality, final long size, final int time, final double bitrate, final double speed) { - final Statistics newStatistics = new Statistics(sessionId, videoFrameNumber, videoFps, videoQuality, size, time, bitrate, speed); + final Statistics statistics = new Statistics(sessionId, videoFrameNumber, videoFps, videoQuality, size, time, bitrate, speed); final Session session = getSession(sessionId); - if (session != null && session.getStatisticsCallback() != null) { - try { - // NOTIFY SESSION CALLBACK IF DEFINED - session.getStatisticsCallback().apply(newStatistics); - } catch (final Exception e) { - android.util.Log.e(FFmpegKitConfig.TAG, String.format("Exception thrown inside session StatisticsCallback block.%s", Exceptions.getStackTraceString(e))); + if (session != null) { + session.addStatistics(statistics); + + if (session.getStatisticsCallback() != null) { + try { + // NOTIFY SESSION CALLBACK IF DEFINED + session.getStatisticsCallback().apply(statistics); + } catch (final Exception e) { + android.util.Log.e(FFmpegKitConfig.TAG, String.format("Exception thrown inside session StatisticsCallback block.%s", Exceptions.getStackTraceString(e))); + } } } @@ -330,7 +365,7 @@ public class FFmpegKitConfig { if (globalStatisticsCallbackFunction != null) { try { // NOTIFY GLOBAL CALLBACK IF DEFINED - globalStatisticsCallbackFunction.apply(newStatistics); + globalStatisticsCallbackFunction.apply(statistics); } catch (final Exception e) { android.util.Log.e(FFmpegKitConfig.TAG, String.format("Exception thrown inside global StatisticsCallback block.%s", Exceptions.getStackTraceString(e))); } @@ -589,7 +624,7 @@ public class FFmpegKitConfig { if (lastSession != null) { // REPLACING CH(13) WITH CH(10) - return lastSession.getLogsAsString().replace('\r', '\n'); + return lastSession.getAllLogsAsString().replace('\r', '\n'); } else { return ""; } @@ -687,8 +722,9 @@ public class FFmpegKitConfig { *

Synchronously executes the media information session provided. * * @param mediaInformationSession media information session which includes command options/arguments + * @param waitTimeout max time to wait until media information is transmitted */ - static void getMediaInformationExecute(final MediaInformationSession mediaInformationSession) { + static void getMediaInformationExecute(final MediaInformationSession mediaInformationSession, final int waitTimeout) { addSession(mediaInformationSession); mediaInformationSession.startRunning(); @@ -696,7 +732,7 @@ public class FFmpegKitConfig { final int returnCode = nativeFFprobeExecute(mediaInformationSession.getSessionId(), mediaInformationSession.getArguments()); mediaInformationSession.complete(returnCode); if (returnCode == ReturnCode.SUCCESS) { - MediaInformation mediaInformation = MediaInformationParser.fromWithError(mediaInformationSession.getLogsAsString()); + MediaInformation mediaInformation = MediaInformationParser.fromWithError(mediaInformationSession.getAllLogsAsString(waitTimeout)); mediaInformationSession.setMediaInformation(mediaInformation); } } catch (final Exception e) { @@ -731,9 +767,10 @@ public class FFmpegKitConfig { *

Asynchronously executes the media information session provided. * * @param mediaInformationSession media information session which includes command options/arguments + * @param waitTimeout max time to wait until media information is transmitted */ - static void asyncGetMediaInformationExecute(final MediaInformationSession mediaInformationSession) { - AsyncGetMediaInformationTask asyncGetMediaInformationTask = new AsyncGetMediaInformationTask(mediaInformationSession); + static void asyncGetMediaInformationExecute(final MediaInformationSession mediaInformationSession, final Integer waitTimeout) { + AsyncGetMediaInformationTask asyncGetMediaInformationTask = new AsyncGetMediaInformationTask(mediaInformationSession, waitTimeout); Future future = asyncExecutorService.submit(asyncGetMediaInformationTask); mediaInformationSession.setFuture(future); } @@ -804,7 +841,7 @@ public class FFmpegKitConfig { * * @return global execute callback function */ - public static ExecuteCallback getGlobalExecuteCallbackFunction() { + static ExecuteCallback getGlobalExecuteCallbackFunction() { return globalExecuteCallbackFunction; } @@ -833,9 +870,16 @@ public class FFmpegKitConfig { *

Converts the given Structured Access Framework Uri ("content:…") into an * input/output url that can be used in FFmpegKit and FFprobeKit. * + *

Requires API Level >= 19. On older API levels it returns an empty url. + * * @return input/output url that can be passed to FFmpegKit or FFprobeKit */ private static String getSafParameter(final Context context, final Uri uri, final String openMode) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + android.util.Log.i(TAG, String.format("getSafParameter is not supported on API Level %d", Build.VERSION.SDK_INT)); + return ""; + } + String displayName = "unknown"; try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { @@ -868,6 +912,8 @@ public class FFmpegKitConfig { *

Converts the given Structured Access Framework Uri ("content:…") into an * input url that can be used in FFmpegKit and FFprobeKit. * + *

Requires API Level >= 19. On older API levels it returns an empty url. + * * @return input url that can be passed to FFmpegKit or FFprobeKit */ public static String getSafParameterForRead(final Context context, final Uri uri) { @@ -878,6 +924,8 @@ public class FFmpegKitConfig { *

Converts the given Structured Access Framework Uri ("content:…") into an * output url that can be used in FFmpegKit and FFprobeKit. * + *

Requires API Level >= 19. On older API levels it returns an empty url. + * * @return output url that can be passed to FFmpegKit or FFprobeKit */ public static String getSafParameterForWrite(final Context context, final Uri uri) { @@ -913,9 +961,16 @@ public class FFmpegKitConfig { /** * Sets the session history size. * - * @param sessionHistorySize new session history size + * @param sessionHistorySize new session history size, should be smaller than 1000 */ - public static void setSessionHistorySize(int sessionHistorySize) { + public static void setSessionHistorySize(final int sessionHistorySize) { + if (sessionHistorySize >= 1000) { + + /* + * THERE IS A HARD LIMIT ON THE NATIVE SIDE. HISTORY SIZE MUST BE SMALLER THAN 1000 + */ + throw new IllegalArgumentException("Session history size must not exceed the hard limit!"); + } FFmpegKitConfig.sessionHistorySize = sessionHistorySize; } @@ -978,27 +1033,17 @@ public class FFmpegKitConfig { * @return all FFmpeg sessions in the session history */ static List getFFmpegSessions() { + final LinkedList list = new LinkedList<>(); + synchronized (sessionHistoryLock) { - return sessionHistoryQueue.stream().filter(new Predicate() { - - @Override - public boolean test(final Session session) { - return (session.isFFmpeg()); + for (Session session : sessionHistoryQueue) { + if (session.isFFmpeg()) { + list.add((FFmpegSession) session); } - }).map(new Function() { - - @Override - public FFmpegSession apply(final Session session) { - return (FFmpegSession) session; - } - }).collect(Collectors.toCollection(new Supplier>() { - - @Override - public List get() { - return new LinkedList<>(); - } - })); + } } + + return list; } /** @@ -1007,27 +1052,17 @@ public class FFmpegKitConfig { * @return all FFprobe sessions in the session history */ static List getFFprobeSessions() { + final LinkedList list = new LinkedList<>(); + synchronized (sessionHistoryLock) { - return sessionHistoryQueue.stream().filter(new Predicate() { - - @Override - public boolean test(final Session session) { - return (session.isFFprobe()); + for (Session session : sessionHistoryQueue) { + if (session.isFFprobe()) { + list.add((FFprobeSession) session); } - }).map(new Function() { - - @Override - public FFprobeSession apply(final Session session) { - return (FFprobeSession) session; - } - }).collect(Collectors.toCollection(new Supplier>() { - - @Override - public List get() { - return new LinkedList<>(); - } - })); + } } + + return list; } /** @@ -1036,21 +1071,35 @@ public class FFmpegKitConfig { * @return sessions that have the given state from the session history */ public static List getSessionsByState(final SessionState state) { + final LinkedList list = new LinkedList<>(); + synchronized (sessionHistoryLock) { - return sessionHistoryQueue.stream().filter(new Predicate() { - - @Override - public boolean test(final Session session) { - return (session.getState() == state); + for (Session session : sessionHistoryQueue) { + if (session.getState() == state) { + list.add(session); } - }).collect(Collectors.toCollection(new Supplier>() { - - @Override - public List get() { - return new LinkedList<>(); - } - })); + } } + + return list; + } + + /** + * Returns the active log redirection strategy. + * + * @return log redirection strategy + */ + public static LogRedirectionStrategy getLogRedirectionStrategy() { + return logRedirectionStrategy; + } + + /** + *

Sets the log redirection strategy + * + * @param logRedirectionStrategy new log redirection strategy + */ + public static void setLogRedirectionStrategy(final LogRedirectionStrategy logRedirectionStrategy) { + FFmpegKitConfig.logRedirectionStrategy = logRedirectionStrategy; } /** @@ -1096,7 +1145,9 @@ public class FFmpegKitConfig { * * @param sessionId id of the session * @param arguments FFmpeg command options/arguments as string array - * @return zero on successful execution, 255 on user cancel and non-zero on error + * @return {@link ReturnCode#SUCCESS} on successful execution and {@link ReturnCode#CANCEL} on + * user cancel. Other non-zero values are returned on error. Use {@link ReturnCode} class to + * handle the value */ private native static int nativeFFmpegExecute(final long sessionId, final String[] arguments); @@ -1113,10 +1164,22 @@ public class FFmpegKitConfig { * * @param sessionId id of the session * @param arguments FFprobe command options/arguments as string array - * @return zero on successful execution, 255 on user cancel and non-zero on error + * @return {@link ReturnCode#SUCCESS} on successful execution and {@link ReturnCode#CANCEL} on + * user cancel. Other non-zero values are returned on error. Use {@link ReturnCode} class to + * handle the value */ native static int nativeFFprobeExecute(final long sessionId, final String[] arguments); + /** + *

Returns the number of native messages which are not transmitted to the Java callbacks for + * this session natively. + * + * @param sessionId id of the session + * @return number of native messages which are not transmitted to the Java callbacks for + * this session natively + */ + native static int messagesInTransmit(final long sessionId); + /** *

Creates a new named pipe to use in FFmpeg operations natively. * diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegSession.java b/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegSession.java index b4d388d..b9c2fe8 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegSession.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/FFmpegSession.java @@ -21,7 +21,6 @@ package com.arthenica.ffmpegkit; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.stream.Stream; /** *

An FFmpeg execute session. @@ -33,19 +32,33 @@ public class FFmpegSession extends AbstractSession implements Session { final ExecuteCallback executeCallback, final LogCallback logCallback, final StatisticsCallback statisticsCallback) { - super(arguments, executeCallback, logCallback, statisticsCallback); + super(arguments, executeCallback, logCallback, statisticsCallback, FFmpegKitConfig.getLogRedirectionStrategy()); this.statistics = new ConcurrentLinkedQueue<>(); } + @Override + public Queue getAllStatistics(final int waitTimeout) { + waitForCallbackMessagesInTransmit(waitTimeout); + + if (thereAreCallbackMessagesInTransmit()) { + android.util.Log.i(FFmpegKitConfig.TAG, String.format("getAllStatistics was asked to return all statistics but there are still statistics being transmitted for session id %d.", sessionId)); + } + + return getStatistics(); + } + + @Override + public Queue getAllStatistics() { + return getAllStatistics(DEFAULT_TIMEOUT_FOR_CALLBACK_MESSAGES_IN_TRANSMIT); + } + + @Override public Queue getStatistics() { return statistics; } - public Stream getStatisticsAsStream() { - return statistics.stream(); - } - + @Override public void addStatistics(final Statistics statistics) { this.statistics.add(statistics); } diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/FFprobeKit.java b/android/app/src/main/java/com/arthenica/ffmpegkit/FFprobeKit.java index d3d7b81..da5f00d 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/FFprobeKit.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/FFprobeKit.java @@ -19,6 +19,7 @@ package com.arthenica.ffmpegkit; +import java.util.List; import java.util.concurrent.Executor; /** @@ -183,6 +184,15 @@ public class FFprobeKit { return session; } + /** + *

Lists all FFprobe sessions in the session history + * + * @return all FFprobe sessions in the session history + */ + public static List listSessions() { + return FFmpegKitConfig.getFFprobeSessions(); + } + /** *

Returns media information for the given path. * @@ -190,7 +200,18 @@ public class FFprobeKit { * @return media information session created for this execution */ public static MediaInformationSession getMediaInformation(final String path) { - return getMediaInformationFromCommandArguments(new String[]{"-v", "error", "-hide_banner", "-print_format", "json", "-show_format", "-show_streams", "-i", path}, null, null, null); + return getMediaInformationFromCommandArguments(new String[]{"-v", "error", "-hide_banner", "-print_format", "json", "-show_format", "-show_streams", "-i", path}, null, null, null, AbstractSession.DEFAULT_TIMEOUT_FOR_CALLBACK_MESSAGES_IN_TRANSMIT); + } + + /** + *

Returns media information for the given path. + * + * @param path path or uri of a media file + * @param waitTimeout max time to wait until media information is transmitted + * @return media information session created for this execution + */ + public static MediaInformationSession getMediaInformation(final String path, final Integer waitTimeout) { + return getMediaInformationFromCommandArguments(new String[]{"-v", "error", "-hide_banner", "-print_format", "json", "-show_format", "-show_streams", "-i", path}, null, null, null, waitTimeout); } /** @@ -204,7 +225,7 @@ public class FFprobeKit { final ExecuteCallback executeCallback) { final MediaInformationSession session = new MediaInformationSession(new String[]{"-v", "error", "-hide_banner", "-print_format", "json", "-show_format", "-show_streams", "-i", path}, executeCallback, null, null); - FFmpegKitConfig.asyncGetMediaInformationExecute(session); + FFmpegKitConfig.asyncGetMediaInformationExecute(session, AbstractSession.DEFAULT_TIMEOUT_FOR_CALLBACK_MESSAGES_IN_TRANSMIT); return session; } @@ -216,15 +237,17 @@ public class FFprobeKit { * @param executeCallback callback that will be notified when execution is completed * @param logCallback callback that will receive log entries * @param statisticsCallback callback that will receive statistics + * @param waitTimeout max time to wait until media information is transmitted * @return media information session created for this execution */ public static MediaInformationSession getMediaInformationAsync(final String path, final ExecuteCallback executeCallback, final LogCallback logCallback, - final StatisticsCallback statisticsCallback) { + final StatisticsCallback statisticsCallback, + final Integer waitTimeout) { final MediaInformationSession session = new MediaInformationSession(new String[]{"-v", "error", "-hide_banner", "-print_format", "json", "-show_format", "-show_streams", "-i", path}, executeCallback, logCallback, statisticsCallback); - FFmpegKitConfig.asyncGetMediaInformationExecute(session); + FFmpegKitConfig.asyncGetMediaInformationExecute(session, waitTimeout); return session; } @@ -256,16 +279,18 @@ public class FFprobeKit { * @param logCallback callback that will receive log entries * @param statisticsCallback callback that will receive statistics * @param executor executor that will be used to run this asynchronous operation + * @param waitTimeout max time to wait until media information is transmitted * @return media information session created for this execution */ public static MediaInformationSession getMediaInformationAsync(final String path, final ExecuteCallback executeCallback, final LogCallback logCallback, final StatisticsCallback statisticsCallback, - final Executor executor) { + final Executor executor, + final Integer waitTimeout) { final MediaInformationSession session = new MediaInformationSession(new String[]{"-v", "error", "-hide_banner", "-print_format", "json", "-show_format", "-show_streams", "-i", path}, executeCallback, logCallback, statisticsCallback); - AsyncGetMediaInformationTask asyncGetMediaInformationTask = new AsyncGetMediaInformationTask(session); + AsyncGetMediaInformationTask asyncGetMediaInformationTask = new AsyncGetMediaInformationTask(session, waitTimeout); executor.execute(asyncGetMediaInformationTask); return session; @@ -278,7 +303,7 @@ public class FFprobeKit { * @return media information session created for this execution */ public static MediaInformationSession getMediaInformationFromCommand(final String command) { - return getMediaInformationFromCommandArguments(FFmpegKit.parseArguments(command), null, null, null); + return getMediaInformationFromCommandArguments(FFmpegKit.parseArguments(command), null, null, null, AbstractSession.DEFAULT_TIMEOUT_FOR_CALLBACK_MESSAGES_IN_TRANSMIT); } @@ -289,22 +314,25 @@ public class FFprobeKit { * @param executeCallback callback that will be notified when execution is completed * @param logCallback callback that will receive log entries * @param statisticsCallback callback that will receive statistics + * @param waitTimeout max time to wait until media information is transmitted * @return media information session created for this execution */ public static MediaInformationSession getMediaInformationFromCommand(final String command, final ExecuteCallback executeCallback, final LogCallback logCallback, - final StatisticsCallback statisticsCallback) { - return getMediaInformationFromCommandArguments(FFmpegKit.parseArguments(command), executeCallback, logCallback, statisticsCallback); + final StatisticsCallback statisticsCallback, + final Integer waitTimeout) { + return getMediaInformationFromCommandArguments(FFmpegKit.parseArguments(command), executeCallback, logCallback, statisticsCallback, waitTimeout); } private static MediaInformationSession getMediaInformationFromCommandArguments(final String[] arguments, final ExecuteCallback executeCallback, final LogCallback logCallback, - final StatisticsCallback statisticsCallback) { + final StatisticsCallback statisticsCallback, + final Integer waitTimeout) { final MediaInformationSession session = new MediaInformationSession(arguments, executeCallback, logCallback, statisticsCallback); - FFmpegKitConfig.getMediaInformationExecute(session); + FFmpegKitConfig.getMediaInformationExecute(session, waitTimeout); return session; } diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/FFprobeSession.java b/android/app/src/main/java/com/arthenica/ffmpegkit/FFprobeSession.java index 0a72666..5f81488 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/FFprobeSession.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/FFprobeSession.java @@ -22,7 +22,6 @@ package com.arthenica.ffmpegkit; import java.text.MessageFormat; import java.util.LinkedList; import java.util.Queue; -import java.util.stream.Stream; /** *

An FFprobe execute session. @@ -33,7 +32,25 @@ public class FFprobeSession extends AbstractSession implements Session { final ExecuteCallback executeCallback, final LogCallback logCallback, final StatisticsCallback statisticsCallback) { - super(arguments, executeCallback, logCallback, statisticsCallback); + this(arguments, executeCallback, logCallback, statisticsCallback, FFmpegKitConfig.getLogRedirectionStrategy()); + } + + FFprobeSession(final String[] arguments, + final ExecuteCallback executeCallback, + final LogCallback logCallback, + final StatisticsCallback statisticsCallback, + final LogRedirectionStrategy logRedirectionStrategy) { + super(arguments, executeCallback, logCallback, statisticsCallback, logRedirectionStrategy); + } + + @Override + public Queue getAllStatistics(final int waitTimeout) { + return new LinkedList<>(); + } + + @Override + public Queue getAllStatistics() { + return new LinkedList<>(); } @Override @@ -41,11 +58,6 @@ public class FFprobeSession extends AbstractSession implements Session { return new LinkedList<>(); } - @Override - public Stream getStatisticsAsStream() { - return new LinkedList().stream(); - } - @Override public void addStatistics(final Statistics statistics) { /* diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/LogRedirectionStrategy.java b/android/app/src/main/java/com/arthenica/ffmpegkit/LogRedirectionStrategy.java new file mode 100644 index 0000000..d9cc00c --- /dev/null +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/LogRedirectionStrategy.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Taner Sener + * + * 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 Public 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 Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FFmpegKit. If not, see . + */ + +package com.arthenica.ffmpegkit; + +public enum LogRedirectionStrategy { + ALWAYS_PRINT_LOGS, + PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED, + PRINT_LOGS_WHEN_GLOBAL_CALLBACK_NOT_DEFINED, + PRINT_LOGS_WHEN_SESSION_CALLBACK_NOT_DEFINED, + NEVER_PRINT_LOGS +} diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/MediaInformationSession.java b/android/app/src/main/java/com/arthenica/ffmpegkit/MediaInformationSession.java index fb94d34..e5d3fbd 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/MediaInformationSession.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/MediaInformationSession.java @@ -30,7 +30,7 @@ public class MediaInformationSession extends FFprobeSession implements Session { final ExecuteCallback executeCallback, final LogCallback logCallback, final StatisticsCallback statisticsCallback) { - super(arguments, executeCallback, logCallback, statisticsCallback); + super(arguments, executeCallback, logCallback, statisticsCallback, LogRedirectionStrategy.NEVER_PRINT_LOGS); } public MediaInformation getMediaInformation() { diff --git a/android/app/src/main/java/com/arthenica/ffmpegkit/Session.java b/android/app/src/main/java/com/arthenica/ffmpegkit/Session.java index fa10329..3bcec42 100644 --- a/android/app/src/main/java/com/arthenica/ffmpegkit/Session.java +++ b/android/app/src/main/java/com/arthenica/ffmpegkit/Session.java @@ -22,7 +22,6 @@ package com.arthenica.ffmpegkit; import java.util.Date; import java.util.Queue; import java.util.concurrent.Future; -import java.util.stream.Stream; /** *

Interface for ffmpeg and ffprobe execute sessions. @@ -49,15 +48,23 @@ public interface Session { String getCommand(); + Queue getAllLogs(final int waitTimeout); + + Queue getAllLogs(); + Queue getLogs(); - Stream getLogsAsStream(); + String getAllLogsAsString(final int waitTimeout); + + String getAllLogsAsString(); String getLogsAsString(); - Queue getStatistics(); + Queue getAllStatistics(final int waitTimeout); - Stream getStatisticsAsStream(); + Queue getAllStatistics(); + + Queue getStatistics(); SessionState getState(); @@ -65,6 +72,10 @@ public interface Session { String getFailStackTrace(); + LogRedirectionStrategy getLogRedirectionStrategy(); + + boolean thereAreCallbackMessagesInTransmit(); + void addLog(final Log log); void addStatistics(final Statistics statistics); diff --git a/android/app/src/test/java/com/arthenica/ffmpegkit/AbstractSessionTest.java b/android/app/src/test/java/com/arthenica/ffmpegkit/AbstractSessionTest.java index e6f26bc..3c749ba 100644 --- a/android/app/src/test/java/com/arthenica/ffmpegkit/AbstractSessionTest.java +++ b/android/app/src/test/java/com/arthenica/ffmpegkit/AbstractSessionTest.java @@ -28,7 +28,7 @@ public class AbstractSessionTest { @Test public void getLogsAsStringTest() { - final FFprobeSession ffprobeSession = new FFprobeSession(TEST_ARGUMENTS, null, null, null); + final FFprobeSession ffprobeSession = new FFprobeSession(TEST_ARGUMENTS, null, null, null, LogRedirectionStrategy.ALWAYS_PRINT_LOGS); String logMessage1 = "i am log one"; String logMessage2 = "i am log two";