Avoid transcoding in MediaPlayers

Test: Manual
Bug: 158466177
Change-Id: I71776e1e81002b1505f5094c92fdd10706d1268e
This commit is contained in:
Zim
2020-09-18 07:55:09 +01:00
parent ae31181f13
commit 95c8a18133
5 changed files with 118 additions and 7 deletions

View File

@@ -40,9 +40,16 @@ import static android.system.OsConstants.W_OK;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.AppGlobals;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.provider.DocumentsContract.Document;
import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
@@ -118,6 +125,7 @@ public final class FileUtils {
// non-final so it can be toggled by Robolectric's ShadowFileUtils
private static boolean sEnableCopyOptimizations = true;
private static volatile int sMediaProviderAppId = -1;
private static final long COPY_CHECKPOINT_BYTES = 524288;
@@ -1424,6 +1432,54 @@ public final class FileUtils {
}
}
/** {@hide} */
public static FileDescriptor convertToModernFd(FileDescriptor fd) {
try {
Context context = AppGlobals.getInitialApplication();
if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
// Never convert modern fd for MediaProvider, because this requires
// MediaStore#scanFile and can cause infinite loops when MediaProvider scans
return null;
}
File realFile = ParcelFileDescriptor.getFile(fd);
Log.i(TAG, "Changing to modern format dataSource for: " + realFile);
ContentResolver resolver = context.getContentResolver();
Uri uri = MediaStore.scanFile(resolver, realFile);
if (uri != null) {
Bundle opts = new Bundle();
// TODO(b/158465539): Use API constant
opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
AssetFileDescriptor afd = resolver.openTypedAssetFileDescriptor(uri, "*/*", opts);
Log.i(TAG, "Changed to modern format dataSource for: " + realFile);
return afd.getFileDescriptor();
} else {
Log.i(TAG, "Failed to change to modern format dataSource for: " + realFile);
}
} catch (Exception e) {
Log.w(TAG, "Failed to change to modern format dataSource");
}
return null;
}
private static int getMediaProviderAppId(Context context) {
if (sMediaProviderAppId != -1) {
return sMediaProviderAppId;
}
PackageManager pm = context.getPackageManager();
ProviderInfo provider = context.getPackageManager().resolveContentProvider(
MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_SYSTEM_ONLY);
if (provider == null) {
return -1;
}
sMediaProviderAppId = UserHandle.getAppId(provider.applicationInfo.uid);
return sMediaProviderAppId;
}
/** {@hide} */
@VisibleForTesting
public static class MemoryPipe extends Thread implements AutoCloseable {

View File

@@ -31,6 +31,8 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.FileUtils;
import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -1523,6 +1525,11 @@ public class ExifInterface {
if (fileDescriptor == null) {
throw new NullPointerException("fileDescriptor cannot be null");
}
boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_exif_optimize", false);
FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fileDescriptor) : null;
if (modernFd != null) {
fileDescriptor = modernFd;
}
mAssetInputStream = null;
mFilename = null;
@@ -2533,11 +2540,20 @@ public class ExifInterface {
private void initForFilename(String filename) throws IOException {
FileInputStream in = null;
FileInputStream legacyInputStream = null;
mAssetInputStream = null;
mFilename = filename;
mIsInputStream = false;
try {
in = new FileInputStream(filename);
boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_exif_optimize",
false);
FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(in.getFD()) : null;
if (modernFd != null) {
legacyInputStream = in;
in = new FileInputStream(modernFd);
}
if (isSeekableFD(in.getFD())) {
mSeekableFileDescriptor = in.getFD();
} else {
@@ -2546,6 +2562,7 @@ public class ExifInterface {
loadAttributes(in);
} finally {
closeQuietly(in);
closeQuietly(legacyInputStream);
}
}

View File

@@ -30,7 +30,10 @@ import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.SystemProperties;
import android.text.TextUtils;
import java.io.FileDescriptor;
@@ -48,7 +51,6 @@ import java.util.Map;
* frame and meta data from an input media file.
*/
public class MediaMetadataRetriever implements AutoCloseable {
// borrowed from ExoPlayer
private static final String[] STANDARD_GENRES = new String[] {
// These are the official ID3v1 genres.
@@ -296,7 +298,19 @@ public class MediaMetadataRetriever implements AutoCloseable {
* non-negative.
* @throws IllegalArgumentException if the arguments are invalid
*/
public native void setDataSource(FileDescriptor fd, long offset, long length)
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IllegalArgumentException {
boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_retriever_optimize",
false);
FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fd) : null;
if (modernFd == null) {
_setDataSource(fd, offset, length);
} else {
_setDataSource(modernFd, offset, length);
}
}
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IllegalArgumentException;
/**
@@ -340,7 +354,12 @@ public class MediaMetadataRetriever implements AutoCloseable {
try {
ContentResolver resolver = context.getContentResolver();
try {
fd = resolver.openAssetFileDescriptor(uri, "r");
boolean optimize =
SystemProperties.getBoolean("fuse.sys.transcode_retriever_optimize", false);
Bundle opts = new Bundle();
opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
fd = optimize ? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts)
: resolver.openAssetFileDescriptor(uri, "r");
} catch(FileNotFoundException e) {
throw new IllegalArgumentException("could not access " + uri);
}

View File

@@ -31,6 +31,8 @@ import android.graphics.SurfaceTexture;
import android.media.SubtitleController.Anchor;
import android.media.SubtitleTrack.RenderingWidget;
import android.net.Uri;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -1104,7 +1106,13 @@ public class MediaPlayer extends PlayerBase
}
private boolean attemptDataSource(ContentResolver resolver, Uri uri) {
try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize",
false);
Bundle opts = new Bundle();
opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
try (AssetFileDescriptor afd = optimize
? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts)
: resolver.openAssetFileDescriptor(uri, "r")) {
setDataSource(afd);
return true;
} catch (NullPointerException | SecurityException | IOException ex) {
@@ -1245,7 +1253,13 @@ public class MediaPlayer extends PlayerBase
*/
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
_setDataSource(fd, offset, length);
boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize", false);
FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fd) : null;
if (modernFd == null) {
_setDataSource(fd, offset, length);
} else {
_setDataSource(modernFd, offset, length);
}
}
private native void _setDataSource(FileDescriptor fd, long offset, long length)
@@ -2899,8 +2913,13 @@ public class MediaPlayer extends PlayerBase
AssetFileDescriptor fd = null;
try {
boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize",
false);
ContentResolver resolver = context.getContentResolver();
fd = resolver.openAssetFileDescriptor(uri, "r");
Bundle opts = new Bundle();
opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
fd = optimize ? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts)
: resolver.openAssetFileDescriptor(uri, "r");
if (fd == null) {
return;
}

View File

@@ -704,7 +704,7 @@ static const JNINativeMethod nativeMethods[] = {
(void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders
},
{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
(void *)android_media_MediaMetadataRetriever_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",
(void *)android_media_MediaMetadataRetriever_setDataSourceCallback},