6262import java .io .InputStream ;
6363import java .io .BufferedInputStream ;
6464import java .io .FileInputStream ;
65+ import android .os .AsyncTask ;
66+ import android .support .annotation .NonNull ;
67+ import android .support .annotation .Nullable ;
6568
6669import java .security .NoSuchAlgorithmException ;
6770import java .security .MessageDigest ;
6871import java .util .Formatter ;
6972
73+
7074public class Trimmer {
7175
7276 private static final String LOG_TAG = "RNTrimmerManager" ;
@@ -77,6 +81,167 @@ public class Trimmer {
7781 private static final int DEFAULT_BUFFER_SIZE = 4096 ;
7882 private static final int END_OF_FILE = -1 ;
7983
84+ private static class FfmpegCmdAsyncTaskParams {
85+ ArrayList <String > cmd ;
86+ final String pathToProcessingFile ;
87+ ReactApplicationContext ctx ;
88+ final Promise promise ;
89+ final String errorMessageTitle ;
90+ final OnCompressVideoListener cb ;
91+
92+ FfmpegCmdAsyncTaskParams (ArrayList <String > cmd , final String pathToProcessingFile , ReactApplicationContext ctx , final Promise promise , final String errorMessageTitle , final OnCompressVideoListener cb ) {
93+ this .cmd = cmd ;
94+ this .pathToProcessingFile = pathToProcessingFile ;
95+ this .ctx = ctx ;
96+ this .promise = promise ;
97+ this .errorMessageTitle = errorMessageTitle ;
98+ this .cb = cb ;
99+ }
100+ }
101+
102+ private static class FfmpegCmdAsyncTask extends AsyncTask <FfmpegCmdAsyncTaskParams , Void , Void > {
103+
104+ @ Override
105+ protected Void doInBackground (FfmpegCmdAsyncTaskParams ... params ) {
106+ ArrayList <String > cmd = params [0 ].cmd ;
107+ final String pathToProcessingFile = params [0 ].pathToProcessingFile ;
108+ ReactApplicationContext ctx = params [0 ].ctx ;
109+ final Promise promise = params [0 ].promise ;
110+ final String errorMessageTitle = params [0 ].errorMessageTitle ;
111+ final OnCompressVideoListener cb = params [0 ].cb ;
112+
113+
114+ String errorMessageFromCmd = null ;
115+
116+ try {
117+ // NOTE: 3. EXECUTE "ffmpeg" COMMAND
118+ String ffmpegInDir = getFfmpegAbsolutePath (ctx );
119+ cmd .add (0 , ffmpegInDir );
120+ Process p = new ProcessBuilder (cmd ).start ();
121+
122+ BufferedReader input = getOutputFromProcess (p );
123+ String line = null ;
124+
125+ StringBuilder sInput = new StringBuilder ();
126+
127+ while ((line =input .readLine ()) != null ) {
128+ Log .d (LOG_TAG , "processing ffmpeg" );
129+ System .out .println (sInput );
130+ sInput .append (line );
131+ }
132+ input .close ();
133+
134+ int errorCode = p .waitFor ();
135+ Log .d (LOG_TAG , "ffmpeg processing completed" );
136+
137+ if ( errorCode != 0 ) {
138+ BufferedReader error = getErrorFromProcess (p );
139+ StringBuilder sError = new StringBuilder ();
140+
141+ Log .d (LOG_TAG , "ffmpeg error code: " + errorCode );
142+ while ((line =error .readLine ()) != null ) {
143+ System .out .println (sError );
144+ sError .append (line );
145+ }
146+ error .close ();
147+
148+ errorMessageFromCmd = sError .toString ();
149+ }
150+ } catch (Exception e ) {
151+ errorMessageFromCmd = e .toString ();
152+ }
153+
154+ if ( errorMessageFromCmd != null ) {
155+ String errorMessage = errorMessageTitle + ": failed. " + errorMessageFromCmd ;
156+ if (cb != null ) {
157+ cb .onError (errorMessage );
158+ } else if (promise != null ) {
159+ promise .reject (errorMessage );
160+ }
161+ } else {
162+ String filePath = "file://" + pathToProcessingFile ;
163+ if (cb != null ) {
164+ cb .onSuccess (filePath );
165+ } else if (promise != null ) {
166+ WritableMap event = Arguments .createMap ();
167+ event .putString ("source" , filePath );
168+ promise .resolve (event );
169+ }
170+ }
171+
172+ return null ;
173+ }
174+
175+ }
176+
177+
178+ private static class LoadFfmpegAsyncTaskParams {
179+ ReactApplicationContext ctx ;
180+
181+ LoadFfmpegAsyncTaskParams (ReactApplicationContext ctx ) {
182+ this .ctx = ctx ;
183+ }
184+ }
185+
186+ private static class LoadFfmpegAsyncTask extends AsyncTask <LoadFfmpegAsyncTaskParams , Void , Void > {
187+
188+ @ Override
189+ protected Void doInBackground (LoadFfmpegAsyncTaskParams ... params ) {
190+ ReactApplicationContext ctx = params [0 ].ctx ;
191+
192+ // NOTE: 1. COPY "ffmpeg" FROM ASSETS TO /data/data/com.myapp...
193+ String filesDir = getFilesDirAbsolutePath (ctx );
194+
195+ try {
196+ File ffmpegFile = new File (filesDir , FFMPEG_FILE_NAME );
197+ if ( !(ffmpegFile .exists () && getSha1FromFile (ffmpegFile ).equalsIgnoreCase (FFMPEG_SHA1 )) ) {
198+ final FileOutputStream ffmpegStreamToDataDir = new FileOutputStream (ffmpegFile );
199+ byte [] buffer = new byte [DEFAULT_BUFFER_SIZE ];
200+
201+ int n ;
202+ InputStream ffmpegInAssets = ctx .getAssets ().open ("armeabi-v7a" + File .separator + FFMPEG_FILE_NAME );
203+ while (END_OF_FILE != (n = ffmpegInAssets .read (buffer ))) {
204+ ffmpegStreamToDataDir .write (buffer , 0 , n );
205+ }
206+
207+ ffmpegStreamToDataDir .flush ();
208+ ffmpegStreamToDataDir .close ();
209+
210+ ffmpegInAssets .close ();
211+ }
212+ } catch (IOException e ) {
213+ Log .d (LOG_TAG , "Failed to copy ffmpeg" + e .toString ());
214+ ffmpegLoaded = false ;
215+ return null ;
216+ }
217+
218+ String ffmpegInDir = getFfmpegAbsolutePath (ctx );
219+
220+ // NOTE: 2. MAKE "ffmpeg" EXECUTABLE
221+ String [] cmdlineChmod = { "chmod" , "700" , ffmpegInDir };
222+ // TODO: 1. CHECK PERMISSIONS
223+ Process pChmod = null ;
224+ try {
225+ pChmod = Runtime .getRuntime ().exec (cmdlineChmod );
226+ } catch (IOException e ) {
227+ Log .d (LOG_TAG , "Failed to make ffmpeg executable. Error in execution cmd. " + e .toString ());
228+ ffmpegLoaded = false ;
229+ return null ;
230+ }
231+
232+ try {
233+ pChmod .waitFor ();
234+ } catch (InterruptedException e ) {
235+ Log .d (LOG_TAG , "Failed to make ffmpeg executable. Error in wait cmd. " + e .toString ());
236+ ffmpegLoaded = false ;
237+ return null ;
238+ }
239+
240+ ffmpegLoaded = true ;
241+ return null ;
242+ }
243+ }
244+
80245
81246 public static void getPreviewImages (String path , Promise promise , ReactApplicationContext ctx ) {
82247 FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever ();
@@ -271,24 +436,7 @@ public static void compress(String source, ReadableMap options, final Promise pr
271436 }
272437 cmd .add (tempFile .getPath ());
273438
274- String error = executeFfmpegCommand (cmd , rctx );
275- if ( error != null ) {
276- if (cb != null ) {
277- cb .onError ("compress error: failed. " + error );
278- } else if (promise != null ) {
279- promise .reject ("compress error: failed." , error );
280- }
281- return ;
282- }
283-
284- if (cb != null ) {
285- cb .onSuccess ("file://" + tempFile .getPath ());
286- } else if (promise != null ) {
287- WritableMap event = Arguments .createMap ();
288- event .putString ("source" , "file://" + tempFile .getPath ());
289- promise .resolve (event );
290- return ;
291- }
439+ executeFfmpegCommand (cmd , tempFile .getPath (), rctx , promise , "compress error" , cb );
292440 }
293441
294442 static File createTempFile (String extension , final Promise promise , Context ctx ) {
@@ -444,59 +592,16 @@ static void crop(String source, ReadableMap options, final Promise promise, Reac
444592 // NOTE: OUTPUT FILE
445593 cmd .add (tempFile .getPath ());
446594
447- String error = executeFfmpegCommand (cmd , ctx );
448- if ( error != null ) {
449- promise .reject ("Crop error" , error );
450- return ;
451- }
452-
453- WritableMap event = Arguments .createMap ();
454- event .putString ("source" , "file://" + tempFile .getPath ());
455- promise .resolve (event );
456- return ;
595+ executeFfmpegCommand (cmd , tempFile .getPath (), ctx , promise , "Crop error" , null );
457596 }
458597
459- static private String executeFfmpegCommand (ArrayList <String > cmd , ReactApplicationContext ctx ) {
460- try {
461- // NOTE: 3. EXECUTE "ffmpeg" COMMAND
462- String ffmpegInDir = getFfmpegAbsolutePath (ctx );
463- cmd .add (0 , ffmpegInDir );
464- Process p = new ProcessBuilder (cmd ).start ();
465-
466- BufferedReader input = getOutputFromProcess (p );
467- String line = null ;
468-
469- StringBuilder sInput = new StringBuilder ();
470-
471- while ((line =input .readLine ()) != null ) {
472- Log .d (LOG_TAG , "processing ffmpeg" );
473- System .out .println (sInput );
474- sInput .append (line );
475- }
476- input .close ();
477-
478- // TODO: DO IT ASYNC
479- int errorCode = p .waitFor ();
480- Log .d (LOG_TAG , "ffmpeg processing completed" );
598+ static private Void executeFfmpegCommand (@ NonNull ArrayList <String > cmd , @ NonNull final String pathToProcessingFile , @ NonNull ReactApplicationContext ctx , @ NonNull final Promise promise , @ NonNull final String errorMessageTitle , @ Nullable final OnCompressVideoListener cb ) {
599+ FfmpegCmdAsyncTaskParams ffmpegCmdAsyncTaskParams = new FfmpegCmdAsyncTaskParams (cmd , pathToProcessingFile , ctx , promise , errorMessageTitle , cb );
481600
482- if ( errorCode != 0 ) {
483- BufferedReader error = getErrorFromProcess (p );
484- StringBuilder sError = new StringBuilder ();
601+ FfmpegCmdAsyncTask ffmpegCmdAsyncTask = new FfmpegCmdAsyncTask ();
602+ ffmpegCmdAsyncTask .execute (ffmpegCmdAsyncTaskParams );
485603
486- Log .d (LOG_TAG , "ffmpeg error code: " + errorCode );
487- while ((line =error .readLine ()) != null ) {
488- System .out .println (sError );
489- sError .append (line );
490- }
491- error .close ();
492-
493- return sError .toString ();
494- }
495-
496- return null ;
497- } catch (Exception e ) {
498- return e .toString ();
499- }
604+ return null ;
500605 }
501606
502607 private static String getFilesDirAbsolutePath (ReactApplicationContext ctx ) {
@@ -538,54 +643,13 @@ public static String getSha1FromFile(final File file) {
538643 }
539644
540645 public static void loadFfmpeg (ReactApplicationContext ctx ) {
541- // NOTE: 1. COPY "ffmpeg" FROM ASSETS TO /data/data/com.myapp...
542- String filesDir = getFilesDirAbsolutePath (ctx );
543-
544- try {
545- File ffmpegFile = new File (filesDir , FFMPEG_FILE_NAME );
546- if ( !(ffmpegFile .exists () && getSha1FromFile (ffmpegFile ).equalsIgnoreCase (FFMPEG_SHA1 )) ) {
547- final FileOutputStream ffmpegStreamToDataDir = new FileOutputStream (ffmpegFile );
548- byte [] buffer = new byte [DEFAULT_BUFFER_SIZE ];
549-
550- int n ;
551- InputStream ffmpegInAssets = ctx .getAssets ().open ("armeabi-v7a" + File .separator + FFMPEG_FILE_NAME );
552- while (END_OF_FILE != (n = ffmpegInAssets .read (buffer ))) {
553- ffmpegStreamToDataDir .write (buffer , 0 , n );
554- }
555-
556- ffmpegStreamToDataDir .flush ();
557- ffmpegStreamToDataDir .close ();
558-
559- ffmpegInAssets .close ();
560- }
561- } catch (IOException e ) {
562- Log .d (LOG_TAG , "Failed to copy ffmpeg" + e .toString ());
563- ffmpegLoaded = false ;
564- return ;
565- }
646+ LoadFfmpegAsyncTaskParams loadFfmpegAsyncTaskParams = new LoadFfmpegAsyncTaskParams (ctx );
566647
567- String ffmpegInDir = getFfmpegAbsolutePath (ctx );
568-
569- // NOTE: 2. MAKE "ffmpeg" EXECUTABLE
570- String [] cmdlineChmod = { "chmod" , "700" , ffmpegInDir };
571- // TODO: 1. CHECK PERMISSIONS. 2. DO IT ASYNC
572- Process pChmod = null ;
573- try {
574- pChmod = Runtime .getRuntime ().exec (cmdlineChmod );
575- } catch (IOException e ) {
576- Log .d (LOG_TAG , "Failed to make ffmpeg executable. Error in execution cmd. " + e .toString ());
577- ffmpegLoaded = false ;
578- return ;
579- }
648+ LoadFfmpegAsyncTask loadFfmpegAsyncTask = new LoadFfmpegAsyncTask ();
649+ loadFfmpegAsyncTask .execute (loadFfmpegAsyncTaskParams );
580650
581- try {
582- pChmod .waitFor ();
583- } catch (InterruptedException e ) {
584- Log .d (LOG_TAG , "Failed to make ffmpeg executable. Error in wait cmd. " + e .toString ());
585- ffmpegLoaded = false ;
586- return ;
587- }
651+ // TODO: EXPOSE TO JS "isFfmpegLoaded" AND "isFfmpegLoading"
588652
589- ffmpegLoaded = true ;
653+ return ;
590654 }
591655}
0 commit comments