You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
367 lines
9.8 KiB
367 lines
9.8 KiB
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package com.arthenica.ffmpegkit;
|
|
|
|
import com.arthenica.smartexception.java.Exceptions;
|
|
|
|
import java.util.Date;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.concurrent.Future;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
/**
|
|
* Abstract session implementation which includes common features shared by <code>FFmpeg</code>
|
|
* and <code>FFprobe</code> sessions.
|
|
*/
|
|
public abstract class AbstractSession implements Session {
|
|
|
|
/**
|
|
* Generates unique ids for sessions.
|
|
*/
|
|
protected static final AtomicLong sessionIdGenerator = new AtomicLong(1);
|
|
|
|
/**
|
|
* Defines how long default "getAll" methods wait, in milliseconds.
|
|
*/
|
|
public static final int DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT = 5000;
|
|
|
|
/**
|
|
* Session identifier.
|
|
*/
|
|
protected final long sessionId;
|
|
|
|
/**
|
|
* Session specific execute callback function.
|
|
*/
|
|
protected final ExecuteCallback executeCallback;
|
|
|
|
/**
|
|
* Session specific log callback function.
|
|
*/
|
|
protected final LogCallback logCallback;
|
|
|
|
/**
|
|
* Date and time the session was created.
|
|
*/
|
|
protected final Date createTime;
|
|
|
|
/**
|
|
* Date and time the session was started.
|
|
*/
|
|
protected Date startTime;
|
|
|
|
/**
|
|
* Date and time the session has ended.
|
|
*/
|
|
protected Date endTime;
|
|
|
|
/**
|
|
* Command arguments as an array.
|
|
*/
|
|
protected final String[] arguments;
|
|
|
|
/**
|
|
* Log entries received for this session.
|
|
*/
|
|
protected final List<Log> logs;
|
|
|
|
/**
|
|
* Log entry lock.
|
|
*/
|
|
protected final Object logsLock;
|
|
|
|
/**
|
|
* Future created for sessions executed asynchronously.
|
|
*/
|
|
protected Future<?> future;
|
|
|
|
/**
|
|
* State of the session.
|
|
*/
|
|
protected SessionState state;
|
|
|
|
/**
|
|
* Return code for the completed sessions.
|
|
*/
|
|
protected ReturnCode returnCode;
|
|
|
|
/**
|
|
* Stack trace of the error received while trying to execute this session.
|
|
*/
|
|
protected String failStackTrace;
|
|
|
|
/**
|
|
* Session specific log redirection strategy.
|
|
*/
|
|
protected final LogRedirectionStrategy logRedirectionStrategy;
|
|
|
|
/**
|
|
* Creates a new abstract session.
|
|
*
|
|
* @param arguments command arguments
|
|
* @param executeCallback session specific execute callback function
|
|
* @param logCallback session specific log callback function
|
|
* @param logRedirectionStrategy session specific log redirection strategy
|
|
*/
|
|
public AbstractSession(final String[] arguments,
|
|
final ExecuteCallback executeCallback,
|
|
final LogCallback logCallback,
|
|
final LogRedirectionStrategy logRedirectionStrategy) {
|
|
this.sessionId = sessionIdGenerator.getAndIncrement();
|
|
this.executeCallback = executeCallback;
|
|
this.logCallback = logCallback;
|
|
this.createTime = new Date();
|
|
this.startTime = null;
|
|
this.endTime = null;
|
|
this.arguments = arguments;
|
|
this.logs = new LinkedList<>();
|
|
this.logsLock = new Object();
|
|
this.future = null;
|
|
this.state = SessionState.CREATED;
|
|
this.returnCode = null;
|
|
this.failStackTrace = null;
|
|
this.logRedirectionStrategy = logRedirectionStrategy;
|
|
}
|
|
|
|
@Override
|
|
public ExecuteCallback getExecuteCallback() {
|
|
return executeCallback;
|
|
}
|
|
|
|
@Override
|
|
public LogCallback getLogCallback() {
|
|
return logCallback;
|
|
}
|
|
|
|
@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;
|
|
if (startTime != null && endTime != null) {
|
|
return (endTime.getTime() - startTime.getTime());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public String[] getArguments() {
|
|
return arguments;
|
|
}
|
|
|
|
@Override
|
|
public String getCommand() {
|
|
return FFmpegKit.argumentsToString(arguments);
|
|
}
|
|
|
|
@Override
|
|
public List<Log> getAllLogs(final int waitTimeout) {
|
|
waitForAsynchronousMessagesInTransmit(waitTimeout);
|
|
|
|
if (thereAreAsynchronousMessagesInTransmit()) {
|
|
android.util.Log.i(FFmpegKitConfig.TAG, String.format("getAllLogs was called to return all logs but there are still logs being transmitted for session id %d.", sessionId));
|
|
}
|
|
|
|
return getLogs();
|
|
}
|
|
|
|
/**
|
|
* Returns all log entries generated for this session. If there are asynchronous
|
|
* messages that are not delivered yet, this method waits for them until
|
|
* {@link #DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT} expires.
|
|
*
|
|
* @return list of log entries generated for this session
|
|
*/
|
|
@Override
|
|
public List<Log> getAllLogs() {
|
|
return getAllLogs(DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT);
|
|
}
|
|
|
|
@Override
|
|
public List<Log> getLogs() {
|
|
synchronized (logsLock) {
|
|
return new LinkedList<>(logs);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String getAllLogsAsString(final int waitTimeout) {
|
|
waitForAsynchronousMessagesInTransmit(waitTimeout);
|
|
|
|
if (thereAreAsynchronousMessagesInTransmit()) {
|
|
android.util.Log.i(FFmpegKitConfig.TAG, String.format("getAllLogsAsString was called to return all logs but there are still logs being transmitted for session id %d.", sessionId));
|
|
}
|
|
|
|
return getLogsAsString();
|
|
}
|
|
|
|
/**
|
|
* Returns all log entries generated for this session as a concatenated string. If there are
|
|
* asynchronous messages that are not delivered yet, this method waits for them until
|
|
* {@link #DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT} expires.
|
|
*
|
|
* @return all log entries generated for this session as a concatenated string
|
|
*/
|
|
@Override
|
|
public String getAllLogsAsString() {
|
|
return getAllLogsAsString(DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT);
|
|
}
|
|
|
|
@Override
|
|
public String getLogsAsString() {
|
|
final StringBuilder concatenatedString = new StringBuilder();
|
|
|
|
synchronized (logsLock) {
|
|
for (Log log : logs) {
|
|
concatenatedString.append(log.getMessage());
|
|
}
|
|
}
|
|
|
|
return concatenatedString.toString();
|
|
}
|
|
|
|
@Override
|
|
public String getOutput() {
|
|
return getAllLogsAsString();
|
|
}
|
|
|
|
@Override
|
|
public SessionState getState() {
|
|
return state;
|
|
}
|
|
|
|
@Override
|
|
public ReturnCode getReturnCode() {
|
|
return returnCode;
|
|
}
|
|
|
|
@Override
|
|
public String getFailStackTrace() {
|
|
return failStackTrace;
|
|
}
|
|
|
|
@Override
|
|
public LogRedirectionStrategy getLogRedirectionStrategy() {
|
|
return logRedirectionStrategy;
|
|
}
|
|
|
|
@Override
|
|
public boolean thereAreAsynchronousMessagesInTransmit() {
|
|
return (FFmpegKitConfig.messagesInTransmit(sessionId) != 0);
|
|
}
|
|
|
|
@Override
|
|
public void addLog(final Log log) {
|
|
synchronized (logsLock) {
|
|
this.logs.add(log);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Future<?> getFuture() {
|
|
return future;
|
|
}
|
|
|
|
@Override
|
|
public void cancel() {
|
|
if (state == SessionState.RUNNING) {
|
|
FFmpegKit.cancel(sessionId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Waits for all asynchronous messages to be transmitted until the given timeout.
|
|
*
|
|
* @param timeout wait timeout in milliseconds
|
|
*/
|
|
protected void waitForAsynchronousMessagesInTransmit(final int timeout) {
|
|
final long start = System.currentTimeMillis();
|
|
|
|
while (thereAreAsynchronousMessagesInTransmit() && (System.currentTimeMillis() < (start + timeout))) {
|
|
synchronized (this) {
|
|
try {
|
|
wait(100);
|
|
} catch (InterruptedException ignored) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the future created for this session.
|
|
*
|
|
* @param future future that runs this session asynchronously
|
|
*/
|
|
void setFuture(final Future<?> future) {
|
|
this.future = future;
|
|
}
|
|
|
|
/**
|
|
* Starts running the session.
|
|
*/
|
|
void startRunning() {
|
|
this.state = SessionState.RUNNING;
|
|
this.startTime = new Date();
|
|
}
|
|
|
|
/**
|
|
* Completes running the session with the provided return code.
|
|
*
|
|
* @param returnCode return code of the execution
|
|
*/
|
|
void complete(final ReturnCode returnCode) {
|
|
this.returnCode = returnCode;
|
|
this.state = SessionState.COMPLETED;
|
|
this.endTime = new Date();
|
|
}
|
|
|
|
/**
|
|
* Ends running the session with a failure.
|
|
*
|
|
* @param exception execution received
|
|
*/
|
|
void fail(final Exception exception) {
|
|
this.failStackTrace = Exceptions.getStackTraceString(exception);
|
|
this.state = SessionState.FAILED;
|
|
this.endTime = new Date();
|
|
}
|
|
|
|
}
|
|
|