34
34
import java .io .File ;
35
35
import java .io .FileOutputStream ;
36
36
import java .io .IOException ;
37
+ import java .net .URI ;
38
+ import java .net .URISyntaxException ;
37
39
import java .text .MessageFormat ;
38
40
import java .util .ArrayList ;
39
41
import java .util .Arrays ;
46
48
import java .util .concurrent .ExecutorService ;
47
49
import java .util .concurrent .Executors ;
48
50
import java .util .concurrent .Future ;
51
+ import java .util .concurrent .atomic .AtomicBoolean ;
49
52
import java .util .concurrent .atomic .AtomicInteger ;
50
53
import java .util .concurrent .atomic .AtomicReference ;
51
54
@@ -128,6 +131,7 @@ public ParcelFileDescriptor getParcelFileDescriptor() {
128
131
private static final SparseArray <SAFProtocolUrl > safIdMap ;
129
132
private static final SparseArray <SAFProtocolUrl > safFileDescriptorMap ;
130
133
private static LogRedirectionStrategy globalLogRedirectionStrategy ;
134
+ private static final AtomicBoolean safUrlsReusable ;
131
135
132
136
static {
133
137
@@ -172,6 +176,7 @@ protected boolean removeEldestEntry(Map.Entry<Long, Session> eldest) {
172
176
safIdMap = new SparseArray <>();
173
177
safFileDescriptorMap = new SparseArray <>();
174
178
globalLogRedirectionStrategy = LogRedirectionStrategy .PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED ;
179
+ safUrlsReusable = new AtomicBoolean (false );
175
180
176
181
android .util .Log .i (FFmpegKitConfig .TAG , String .format ("Loaded ffmpeg-kit-%s-%s-%s-%s." , NativeLoader .loadPackageName (), NativeLoader .loadAbi (), NativeLoader .loadVersion (), NativeLoader .loadBuildDate ()));
177
182
}
@@ -487,7 +492,8 @@ public static void setFontDirectoryList(final Context context, final List<String
487
492
/**
488
493
* <p>Creates a new named pipe to use in <code>FFmpeg</code> operations.
489
494
*
490
- * <p>Please note that creator is responsible of closing created pipes.
495
+ * <p>Please note that creator is responsible of closing created pipes via the
496
+ * {@link #closeFFmpegPipe} method.
491
497
*
492
498
* @param context application context
493
499
* @return the full path of the named pipe
@@ -1029,6 +1035,7 @@ private static int safOpen(final int safId) {
1029
1035
safUrl .setParcelFileDescriptor (parcelFileDescriptor );
1030
1036
final int fd = parcelFileDescriptor .getFd ();
1031
1037
safFileDescriptorMap .put (fd , safUrl );
1038
+ android .util .Log .d (TAG , String .format ("Generated fd %d for SAF id %d." , fd , safId ));
1032
1039
return fd ;
1033
1040
} else {
1034
1041
android .util .Log .e (TAG , String .format ("SAF id %d not found." , safId ));
@@ -1050,11 +1057,15 @@ private static int safClose(final int fileDescriptor) {
1050
1057
try {
1051
1058
final SAFProtocolUrl safProtocolUrl = safFileDescriptorMap .get (fileDescriptor );
1052
1059
if (safProtocolUrl != null ) {
1060
+ final int safId = safProtocolUrl .getSafId ();
1053
1061
ParcelFileDescriptor parcelFileDescriptor = safProtocolUrl .getParcelFileDescriptor ();
1054
1062
if (parcelFileDescriptor != null ) {
1055
1063
safFileDescriptorMap .delete (fileDescriptor );
1056
- safIdMap .delete (safProtocolUrl .getSafId ());
1057
1064
parcelFileDescriptor .close ();
1065
+ if (!safUrlsReusable .get ()) {
1066
+ safIdMap .delete (safId );
1067
+ }
1068
+ android .util .Log .d (TAG , String .format ("Closed fd %d for SAF id %d." , fileDescriptor , safId ));
1058
1069
return 1 ;
1059
1070
} else {
1060
1071
android .util .Log .e (TAG , String .format ("ParcelFileDescriptor for SAF fd %d not found." , fileDescriptor ));
@@ -1069,6 +1080,31 @@ private static int safClose(final int fileDescriptor) {
1069
1080
return 0 ;
1070
1081
}
1071
1082
1083
+ /**
1084
+ * Unregisters saf protocol urls and cleans up the resources associated with them.
1085
+ *
1086
+ * @param safUrl saf protocol url e.g. saf:1.mp4
1087
+ */
1088
+ public static void unregisterSafProtocolUrl (final String safUrl ) {
1089
+ if (safUrl != null ) {
1090
+ try {
1091
+ URI uri = new URI (safUrl );
1092
+ String path = uri .getSchemeSpecificPart ();
1093
+ int index = path .indexOf ("." );
1094
+ if (index > -1 ) {
1095
+ String safIdString = path .substring (0 , index );
1096
+ int safId = Integer .parseInt (safIdString );
1097
+ safIdMap .delete (safId );
1098
+ android .util .Log .d (TAG , String .format ("Unregistered safUrl %s successfully." , safUrl ));
1099
+ } else {
1100
+ android .util .Log .w (FFmpegKitConfig .TAG , String .format ("Cannot unregister safUrl %s. Failed to drop extension!" , safUrl ));
1101
+ }
1102
+ } catch (URISyntaxException | NumberFormatException e ) {
1103
+ android .util .Log .w (FFmpegKitConfig .TAG , String .format ("Cannot unregister safUrl %s. Failed to extract saf id!" , safUrl ));
1104
+ }
1105
+ }
1106
+ }
1107
+
1072
1108
/**
1073
1109
* Returns the session history size.
1074
1110
*
@@ -1295,6 +1331,31 @@ public static void setLogRedirectionStrategy(final LogRedirectionStrategy logRed
1295
1331
FFmpegKitConfig .globalLogRedirectionStrategy = logRedirectionStrategy ;
1296
1332
}
1297
1333
1334
+ /**
1335
+ * Returns if SAF protocol urls are reusable or not.
1336
+ *
1337
+ * @return true if SAF protocol urls are reusable, false otherwise
1338
+ */
1339
+ public static boolean getSafUrlsReusable () {
1340
+ return safUrlsReusable .get ();
1341
+ }
1342
+
1343
+ /**
1344
+ * Defines whether the generated SAF protocol urls will be reusable or not.
1345
+ *
1346
+ * <p>Note that SAF protocol urls are not reusable by default and are automatically
1347
+ * unregistered when the file associated with them is closed.
1348
+ *
1349
+ * <p>If they are set to be reused using this method, automatic unregistration will be
1350
+ * disabled. Therefore, it will be the developer's responsibility to unregister them via the
1351
+ * {@link #unregisterSafProtocolUrl} method.
1352
+ *
1353
+ * @param safUrlsReusable set to true to enable the reuse of SAF protocol urls
1354
+ */
1355
+ public static void setSafUrlsReusable (final boolean safUrlsReusable ) {
1356
+ FFmpegKitConfig .safUrlsReusable .compareAndSet (!safUrlsReusable , safUrlsReusable );
1357
+ }
1358
+
1298
1359
/**
1299
1360
* Converts session state to string.
1300
1361
*
0 commit comments