implement execute methods for react-native

This commit is contained in:
Taner Sener 2021-12-22 22:36:15 +00:00
parent 2fd7097e23
commit ea9f267ce6
8 changed files with 409 additions and 10 deletions

View File

@ -121,14 +121,14 @@ public class FFmpegKitReactNativeModule extends ReactContextBaseJavaModule imple
private final AtomicBoolean logsEnabled;
private final AtomicBoolean statisticsEnabled;
private final ExecutorService asyncWriteToPipeExecutorService;
private final ExecutorService asyncExecutorService;
public FFmpegKitReactNativeModule(@Nullable ReactApplicationContext reactContext) {
super(reactContext);
this.logsEnabled = new AtomicBoolean(false);
this.statisticsEnabled = new AtomicBoolean(false);
this.asyncWriteToPipeExecutorService = Executors.newFixedThreadPool(asyncWriteToPipeConcurrencyLimit);
this.asyncExecutorService = Executors.newFixedThreadPool(asyncWriteToPipeConcurrencyLimit);
if (reactContext != null) {
reactContext.addLifecycleEventListener(this);
@ -161,7 +161,7 @@ public class FFmpegKitReactNativeModule extends ReactContextBaseJavaModule imple
@Override
public void onHostDestroy() {
this.asyncWriteToPipeExecutorService.shutdown();
this.asyncExecutorService.shutdown();
}
protected void registerGlobalCallbacks(final ReactApplicationContext reactContext) {
@ -573,6 +573,69 @@ public class FFmpegKitReactNativeModule extends ReactContextBaseJavaModule imple
}
}
@ReactMethod
public void ffmpegSessionExecute(final Double sessionId, final Promise promise) {
if (sessionId != null) {
Session session = FFmpegKitConfig.getSession(sessionId.longValue());
if (session == null) {
promise.reject("SESSION_NOT_FOUND", "Session not found.");
} else {
if (session instanceof FFmpegSession) {
final FFmpegSessionExecuteTask ffmpegSessionExecuteTask = new FFmpegSessionExecuteTask((FFmpegSession) session, promise);
asyncExecutorService.submit(ffmpegSessionExecuteTask);
} else {
promise.reject("NOT_FFMPEG_SESSION", "A session is found but it does not have the correct type.");
}
}
} else {
promise.reject("INVALID_SESSION", "Invalid session id.");
}
}
@ReactMethod
public void ffprobeSessionExecute(final Double sessionId, final Promise promise) {
if (sessionId != null) {
Session session = FFmpegKitConfig.getSession(sessionId.longValue());
if (session == null) {
promise.reject("SESSION_NOT_FOUND", "Session not found.");
} else {
if (session instanceof FFprobeSession) {
final FFprobeSessionExecuteTask ffprobeSessionExecuteTask = new FFprobeSessionExecuteTask((FFprobeSession) session, promise);
asyncExecutorService.submit(ffprobeSessionExecuteTask);
} else {
promise.reject("NOT_FFPROBE_SESSION", "A session is found but it does not have the correct type.");
}
}
} else {
promise.reject("INVALID_SESSION", "Invalid session id.");
}
}
@ReactMethod
public void mediaInformationSessionExecute(final Double sessionId, final Double waitTimeout, final Promise promise) {
if (sessionId != null) {
Session session = FFmpegKitConfig.getSession(sessionId.longValue());
if (session == null) {
promise.reject("SESSION_NOT_FOUND", "Session not found.");
} else {
if (session instanceof MediaInformationSession) {
final int timeout;
if (isValidPositiveNumber(waitTimeout)) {
timeout = waitTimeout.intValue();
} else {
timeout = AbstractSession.DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT;
}
final MediaInformationSessionExecuteTask mediaInformationSessionExecuteTask = new MediaInformationSessionExecuteTask((MediaInformationSession) session, timeout, promise);
asyncExecutorService.submit(mediaInformationSessionExecuteTask);
} else {
promise.reject("NOT_MEDIA_INFORMATION_SESSION", "A session is found but it does not have the correct type.");
}
}
} else {
promise.reject("INVALID_SESSION", "Invalid session id.");
}
}
@ReactMethod
public void asyncFFmpegSessionExecute(final Double sessionId, final Promise promise) {
if (sessionId != null) {
@ -743,8 +806,8 @@ public class FFmpegKitReactNativeModule extends ReactContextBaseJavaModule imple
@ReactMethod
public void writeToPipe(final String inputPath, final String namedPipePath, final Promise promise) {
final AsyncWriteToPipeTask asyncTask = new AsyncWriteToPipeTask(inputPath, namedPipePath, promise);
asyncWriteToPipeExecutorService.submit(asyncTask);
final WriteToPipeTask asyncTask = new WriteToPipeTask(inputPath, namedPipePath, promise);
asyncExecutorService.submit(asyncTask);
}
@ReactMethod

View File

@ -0,0 +1,40 @@
/*
* 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.reactnative;
import com.arthenica.ffmpegkit.FFmpegKitConfig;
import com.arthenica.ffmpegkit.FFmpegSession;
import com.facebook.react.bridge.Promise;
public class FFmpegSessionExecuteTask implements Runnable {
private final FFmpegSession ffmpegSession;
private final Promise promise;
public FFmpegSessionExecuteTask(final FFmpegSession ffmpegSession, final Promise promise) {
this.ffmpegSession = ffmpegSession;
this.promise = promise;
}
@Override
public void run() {
FFmpegKitConfig.ffmpegExecute(ffmpegSession);
promise.resolve(null);
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.reactnative;
import com.arthenica.ffmpegkit.FFmpegKitConfig;
import com.arthenica.ffmpegkit.FFprobeSession;
import com.facebook.react.bridge.Promise;
public class FFprobeSessionExecuteTask implements Runnable {
private final FFprobeSession ffprobeSession;
private final Promise promise;
public FFprobeSessionExecuteTask(final FFprobeSession ffprobeSession, final Promise promise) {
this.ffprobeSession = ffprobeSession;
this.promise = promise;
}
@Override
public void run() {
FFmpegKitConfig.ffprobeExecute(ffprobeSession);
promise.resolve(null);
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.reactnative;
import com.arthenica.ffmpegkit.FFmpegKitConfig;
import com.arthenica.ffmpegkit.MediaInformationSession;
import com.facebook.react.bridge.Promise;
public class MediaInformationSessionExecuteTask implements Runnable {
private final MediaInformationSession mediaInformationSession;
private final int timeout;
private final Promise promise;
public MediaInformationSessionExecuteTask(final MediaInformationSession mediaInformationSession, final int timeout, final Promise promise) {
this.mediaInformationSession = mediaInformationSession;
this.timeout = timeout;
this.promise = promise;
}
@Override
public void run() {
FFmpegKitConfig.getMediaInformationExecute(mediaInformationSession, timeout);
promise.resolve(null);
}
}

View File

@ -27,12 +27,12 @@ import com.facebook.react.bridge.Promise;
import java.io.IOException;
public class AsyncWriteToPipeTask implements Runnable {
public class WriteToPipeTask implements Runnable {
private final String inputPath;
private final String namedPipePath;
private final Promise promise;
public AsyncWriteToPipeTask(final String inputPath, final String namedPipePath, final Promise promise) {
public WriteToPipeTask(final String inputPath, final String namedPipePath, final Promise promise) {
this.inputPath = inputPath;
this.namedPipePath = namedPipePath;
this.promise = promise;

View File

@ -68,7 +68,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
@implementation FFmpegKitReactNativeModule {
BOOL logsEnabled;
BOOL statisticsEnabled;
dispatch_queue_t asyncWriteToPipeDispatchQueue;
dispatch_queue_t asyncDispatchQueue;
}
RCT_EXPORT_MODULE(FFmpegKitReactNativeModule);
@ -78,7 +78,7 @@ RCT_EXPORT_MODULE(FFmpegKitReactNativeModule);
if (self) {
logsEnabled = false;
statisticsEnabled = false;
asyncWriteToPipeDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
asyncDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self registerGlobalCallbacks];
}
@ -404,6 +404,59 @@ RCT_EXPORT_METHOD(ignoreSignal:(int)signalValue resolver:(RCTPromiseResolveBlock
}
}
RCT_EXPORT_METHOD(ffmpegSessionExecute:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
if ([session isMemberOfClass:[FFmpegSession class]]) {
dispatch_async(asyncDispatchQueue, ^{
[FFmpegKitConfig ffmpegExecute:(FFmpegSession*)session];
resolve(nil);
});
} else {
reject(@"NOT_FFMPEG_SESSION", @"A session is found but it does not have the correct type.", nil);
}
}
}
RCT_EXPORT_METHOD(ffprobeSessionExecute:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
if ([session isMemberOfClass:[FFprobeSession class]]) {
dispatch_async(asyncDispatchQueue, ^{
[FFmpegKitConfig ffprobeExecute:(FFprobeSession*)session];
resolve(nil);
});
} else {
reject(@"NOT_FFPROBE_SESSION", @"A session is found but it does not have the correct type.", nil);
}
}
}
RCT_EXPORT_METHOD(mediaInformationSessionExecute:(int)sessionId withTimeout:(int)waitTimeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
reject(@"SESSION_NOT_FOUND", @"Session not found.", nil);
} else {
if ([session isMemberOfClass:[MediaInformationSession class]]) {
int timeout;
if ([FFmpegKitReactNativeModule isValidPositiveNumber:waitTimeout]) {
timeout = waitTimeout;
} else {
timeout = AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit;
}
dispatch_async(asyncDispatchQueue, ^{
[FFmpegKitConfig getMediaInformationExecute:(MediaInformationSession*)session withTimeout:timeout];
resolve(nil);
});
} else {
reject(@"NOT_MEDIA_INFORMATION_SESSION", @"A session is found but it does not have the correct type.", nil);
}
}
}
RCT_EXPORT_METHOD(asyncFFmpegSessionExecute:(int)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:sessionId];
if (session == nil) {
@ -518,7 +571,7 @@ RCT_EXPORT_METHOD(getPlatform:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromi
}
RCT_EXPORT_METHOD(writeToPipe:(NSString*)inputPath onPipe:(NSString*)namedPipePath resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
dispatch_async(asyncWriteToPipeDispatchQueue, ^{
dispatch_async(asyncDispatchQueue, ^{
NSLog(@"Starting copy %@ to pipe %@ operation.\n", inputPath, namedPipePath);

View File

@ -72,6 +72,10 @@ declare module 'ffmpeg-kit-react-native' {
export class FFmpegKit {
static execute(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, statisticsCallback?: StatisticsCallback): Promise<FFmpegSession>;
static executeWithArguments(commandArguments: string[], executeCallback?: ExecuteCallback, logCallback?: LogCallback, statisticsCallback?: StatisticsCallback): Promise<FFmpegSession>;
static executeAsync(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, statisticsCallback?: StatisticsCallback): Promise<FFmpegSession>;
static executeWithArgumentsAsync(commandArguments: string[], executeCallback?: ExecuteCallback, logCallback?: LogCallback, statisticsCallback?: StatisticsCallback): Promise<FFmpegSession>;
@ -112,6 +116,12 @@ declare module 'ffmpeg-kit-react-native' {
static ignoreSignal(signal: Signal): Promise<void>;
static ffmpegExecute(session: FFmpegSession): Promise<void>;
static ffprobeExecute(session: FFprobeSession): Promise<void>;
static getMediaInformationExecute(session: MediaInformationSession, waitTimeout?: number): Promise<void>;
static asyncFFmpegExecute(session: FFmpegSession): Promise<void>;
static asyncFFprobeExecute(session: FFprobeSession): Promise<void>;
@ -202,10 +212,20 @@ declare module 'ffmpeg-kit-react-native' {
export class FFprobeKit {
static execute(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback): Promise<FFprobeSession>;
static executeWithArguments(commandArguments: string[], executeCallback?: ExecuteCallback, logCallback?: LogCallback): Promise<FFprobeSession>;
static executeAsync(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback): Promise<FFprobeSession>;
static executeWithArgumentsAsync(commandArguments: string[], executeCallback?: ExecuteCallback, logCallback?: LogCallback): Promise<FFprobeSession>;
static getMediaInformation(path: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, waitTimeout?: number): Promise<MediaInformationSession>;
static getMediaInformationFromCommand(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, waitTimeout?: number): Promise<MediaInformationSession>;
static getMediaInformationFromCommandArguments(commandArguments: string[], executeCallback?: ExecuteCallback, logCallback?: LogCallback, waitTimeout?: number): Promise<MediaInformationSession>;
static getMediaInformationAsync(path: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, waitTimeout?: number): Promise<MediaInformationSession>;
static getMediaInformationFromCommandAsync(command: string, executeCallback?: ExecuteCallback, logCallback?: LogCallback, waitTimeout?: number): Promise<MediaInformationSession>;

View File

@ -702,6 +702,37 @@ export class ArchDetect {
*/
export class FFmpegKit {
/**
* <p>Synchronously executes FFmpeg command provided. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* @param command FFmpeg command
* @param executeCallback callback that will be called when the execution is completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @return FFmpeg session created for this execution
*/
static async execute(command, executeCallback, logCallback, statisticsCallback) {
return FFmpegKit.executeWithArguments(FFmpegKitConfig.parseArguments(command), executeCallback, logCallback, statisticsCallback);
}
/**
* <p>Synchronously executes FFmpeg with arguments provided.
*
* @param commandArguments FFmpeg command options/arguments as string array
* @param executeCallback callback that will be called when the execution is completed
* @param logCallback callback that will receive logs
* @param statisticsCallback callback that will receive statistics
* @return FFmpeg session created for this execution
*/
static async executeWithArguments(commandArguments, executeCallback, logCallback, statisticsCallback) {
let session = await FFmpegSession.create(commandArguments, executeCallback, logCallback, statisticsCallback);
await FFmpegKitConfig.ffmpegExecute(session);
return session;
}
/**
* <p>Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
@ -950,6 +981,40 @@ export class FFmpegKitConfig {
return FFmpegKitReactNativeModule.ignoreSignal(signal);
}
/**
* <p>Synchronously executes the FFmpeg session provided.
*
* @param ffmpegSession FFmpeg session which includes command options/arguments
*/
static async ffmpegExecute(ffmpegSession) {
await FFmpegKitConfig.init();
return FFmpegKitReactNativeModule.ffmpegSessionExecute(ffmpegSession.getSessionId());
}
/**
* <p>Synchronously executes the FFprobe session provided.
*
* @param ffprobeSession FFprobe session which includes command options/arguments
*/
static async ffprobeExecute(ffprobeSession) {
await FFmpegKitConfig.init();
return FFmpegKitReactNativeModule.ffprobeSessionExecute(ffprobeSession.getSessionId());
}
/**
* <p>Synchronously executes the media information session provided.
*
* @param mediaInformationSession media information session which includes command options/arguments
* @param waitTimeout max time to wait until media information is transmitted
*/
static async getMediaInformationExecute(mediaInformationSession, waitTimeout) {
await FFmpegKitConfig.init();
return FFmpegKitReactNativeModule.mediaInformationSessionExecute(mediaInformationSession.getSessionId(), FFmpegKitFactory.optionalNumericParameter(waitTimeout));
}
/**
* <p>Starts an asynchronous FFmpeg execution for the given session.
*
@ -1809,6 +1874,35 @@ export class FFmpegSession extends AbstractSession {
*/
export class FFprobeKit {
/**
* <p>Synchronously executes FFprobe command provided. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
*
* @param command FFprobe command
* @param executeCallback callback that will be called when the execution is completed
* @param logCallback callback that will receive logs
* @return FFprobe session created for this execution
*/
static async execute(command, executeCallback, logCallback) {
return FFprobeKit.executeWithArguments(FFmpegKitConfig.parseArguments(command), executeCallback, logCallback);
}
/**
* <p>Synchronously executes FFprobe with arguments provided.
*
* @param commandArguments FFprobe command options/arguments as string array
* @param executeCallback callback that will be called when the execution is completed
* @param logCallback callback that will receive logs
* @return FFprobe session created for this execution
*/
static async executeWithArguments(commandArguments, executeCallback, logCallback) {
let session = await FFprobeSession.create(commandArguments, executeCallback, logCallback);
await FFmpegKitConfig.ffprobeExecute(session);
return session;
}
/**
* <p>Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command
* into arguments. You can use single or double quote characters to specify arguments inside your command.
@ -1844,6 +1938,53 @@ export class FFprobeKit {
return session;
}
/**
* <p>Extracts media information for the file specified with path.
*
* @param path path or uri of a media file
* @param executeCallback callback that will be notified when execution is completed
* @param logCallback callback that will receive logs
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
static async getMediaInformation(path, executeCallback, logCallback, waitTimeout) {
const commandArguments = ["-v", "error", "-hide_banner", "-print_format", "json", "-show_format", "-show_streams", "-show_chapters", "-i", path];
return FFprobeKit.getMediaInformationFromCommandArguments(commandArguments, executeCallback, logCallback, waitTimeout);
}
/**
* <p>Extracts media information using the command provided. The command passed to
* this method must generate the output in JSON format in order to successfully extract media information from it.
*
* @param command FFprobe command that prints media information for a file in JSON format
* @param executeCallback callback that will be notified when execution is completed
* @param logCallback callback that will receive logs
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
static async getMediaInformationFromCommand(command, executeCallback, logCallback, waitTimeout) {
return FFprobeKit.getMediaInformationFromCommandArguments(FFmpegKitConfig.parseArguments(command), executeCallback, logCallback, waitTimeout);
}
/**
* <p>Extracts media information using the command arguments provided. The command
* passed to this method must generate the output in JSON format in order to successfully extract media information
* from it.
*
* @param commandArguments FFprobe command arguments that prints media information for a file in JSON format
* @param executeCallback callback that will be notified when execution is completed
* @param logCallback callback that will receive logs
* @param waitTimeout max time to wait until media information is transmitted
* @return media information session created for this execution
*/
static async getMediaInformationFromCommandArguments(commandArguments, executeCallback, logCallback, waitTimeout) {
let session = await MediaInformationSession.create(commandArguments, executeCallback, logCallback);
await FFmpegKitConfig.getMediaInformationExecute(session, waitTimeout);
return session;
}
/**
* <p>Starts an asynchronous FFprobe execution to extract the media information for the specified file.
*