Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.

Commit c6e08c0

Browse files
committed
implement reusable saf urls, fixes #911
1 parent b4130e7 commit c6e08c0

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

android/ffmpeg-kit-android-lib/src/main/java/com/arthenica/ffmpegkit/FFmpegKitConfig.java

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import java.io.File;
3535
import java.io.FileOutputStream;
3636
import java.io.IOException;
37+
import java.net.URI;
38+
import java.net.URISyntaxException;
3739
import java.text.MessageFormat;
3840
import java.util.ArrayList;
3941
import java.util.Arrays;
@@ -46,6 +48,7 @@
4648
import java.util.concurrent.ExecutorService;
4749
import java.util.concurrent.Executors;
4850
import java.util.concurrent.Future;
51+
import java.util.concurrent.atomic.AtomicBoolean;
4952
import java.util.concurrent.atomic.AtomicInteger;
5053
import java.util.concurrent.atomic.AtomicReference;
5154

@@ -128,6 +131,7 @@ public ParcelFileDescriptor getParcelFileDescriptor() {
128131
private static final SparseArray<SAFProtocolUrl> safIdMap;
129132
private static final SparseArray<SAFProtocolUrl> safFileDescriptorMap;
130133
private static LogRedirectionStrategy globalLogRedirectionStrategy;
134+
private static final AtomicBoolean safUrlsReusable;
131135

132136
static {
133137

@@ -172,6 +176,7 @@ protected boolean removeEldestEntry(Map.Entry<Long, Session> eldest) {
172176
safIdMap = new SparseArray<>();
173177
safFileDescriptorMap = new SparseArray<>();
174178
globalLogRedirectionStrategy = LogRedirectionStrategy.PRINT_LOGS_WHEN_NO_CALLBACKS_DEFINED;
179+
safUrlsReusable = new AtomicBoolean(false);
175180

176181
android.util.Log.i(FFmpegKitConfig.TAG, String.format("Loaded ffmpeg-kit-%s-%s-%s-%s.", NativeLoader.loadPackageName(), NativeLoader.loadAbi(), NativeLoader.loadVersion(), NativeLoader.loadBuildDate()));
177182
}
@@ -487,7 +492,8 @@ public static void setFontDirectoryList(final Context context, final List<String
487492
/**
488493
* <p>Creates a new named pipe to use in <code>FFmpeg</code> operations.
489494
*
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.
491497
*
492498
* @param context application context
493499
* @return the full path of the named pipe
@@ -1029,6 +1035,7 @@ private static int safOpen(final int safId) {
10291035
safUrl.setParcelFileDescriptor(parcelFileDescriptor);
10301036
final int fd = parcelFileDescriptor.getFd();
10311037
safFileDescriptorMap.put(fd, safUrl);
1038+
android.util.Log.d(TAG, String.format("Generated fd %d for SAF id %d.", fd, safId));
10321039
return fd;
10331040
} else {
10341041
android.util.Log.e(TAG, String.format("SAF id %d not found.", safId));
@@ -1050,11 +1057,15 @@ private static int safClose(final int fileDescriptor) {
10501057
try {
10511058
final SAFProtocolUrl safProtocolUrl = safFileDescriptorMap.get(fileDescriptor);
10521059
if (safProtocolUrl != null) {
1060+
final int safId = safProtocolUrl.getSafId();
10531061
ParcelFileDescriptor parcelFileDescriptor = safProtocolUrl.getParcelFileDescriptor();
10541062
if (parcelFileDescriptor != null) {
10551063
safFileDescriptorMap.delete(fileDescriptor);
1056-
safIdMap.delete(safProtocolUrl.getSafId());
10571064
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));
10581069
return 1;
10591070
} else {
10601071
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) {
10691080
return 0;
10701081
}
10711082

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+
10721108
/**
10731109
* Returns the session history size.
10741110
*
@@ -1295,6 +1331,31 @@ public static void setLogRedirectionStrategy(final LogRedirectionStrategy logRed
12951331
FFmpegKitConfig.globalLogRedirectionStrategy = logRedirectionStrategy;
12961332
}
12971333

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+
12981359
/**
12991360
* Converts session state to string.
13001361
*

android/ffmpeg-kit-android-lib/src/test/java/com/arthenica/ffmpegkit/FFmpegKitConfigTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ public void setSessionHistorySize() {
187187
}
188188
}
189189

190+
@Test
191+
public void unregisterSafProtocolUrl() {
192+
FFmpegKitConfig.unregisterSafProtocolUrl("saf:1.mp4");
193+
}
194+
190195
private String listToPackageName(final List<String> externalLibraryList) {
191196
boolean speex = externalLibraryList.contains("speex");
192197
boolean fribidi = externalLibraryList.contains("fribidi");

0 commit comments

Comments
 (0)