I'm recording video with the aid of android.media.MediaRecorder
class, which accepts path string for output file (MediaRecorder.setOutputFile(String)
), though there is a version of the method which accepts FileDescriptor
.
I need to store huge video files, so I want to use SD card. In order to get path to relevant directory I use Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
. It turns out that resulting path is for the “emulated” storage (/sdcard/…
) instead of real SD card (/sdcard1/
on my Xperia Z3 Compact, Android 5.1.1).
I tried to hardcode /sdcard1/
(as well as /storage/sdcard1
) but get an IOException
talking about permission deny. Of course, I have
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I heard about huge changes in access to SD card after 4.4 (aka Storage Access Framework), but couldn't find simple and clear enough explanation on how to get things done in such a case. Any help on short and concise solution for this?
PS Solution with hardcoded path would be OK for me as I'm going to use this app only with my phone.
This code should solve your problem:
public String createVideoFilePath() {
String time = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File[] pathsArray = getExternalFilesDirs(Environment.DIRECTORY_DCIM);
/Usually, pathArray[1] contains path to
necessary folder on removable SD
card. So we can generally use just pathArray[1] instead of searching necessary File in pathArray with for statement/
for (File f : pathsArray) {
if ((f != null) && (Environment.isExternalStorageRemovable(f))) {
return f.getPath() + File.separator +"video_"+ time + ".mp4";
}
}
return pathsArray[0].getPath() + File.separator +"video_"+ time + ".mp4";
}
This method returns location of file on removable SD card or in emulated storage, if removable SD doesn't exist
Just use this method as a paramter of MediaRecorder.setOutputFile(String)
Update:
It is very important to say, that all files, which locate in folders, gotten with Context. getExternalFilesDirs(String typr)
will be removed after uninstalling your app.
So I had to go “full circle” with above mentioned Storage Access Framework.
Step 1: Request for file creation
Note: fileName
below is literally name of a file itself, without full (desired) path to it. E.g. myvideo.mp4
.
private void createFile(String fileName) {
String mimeType = "video/mp4";
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(mimeType);
intent.putExtra(Intent.EXTRA_TITLE, fileName);
startActivityForResult(intent, WRITE_REQUEST_CODE /* is some random int constant*/);
}
Step 2: Callback for handling directory choice
After calling createFile
(cf. step 1) a user will be supplied with a system dialog for choosing a directory where file will be stored. After they makes their choice the handler will be fired with desired URI for the new file. We can turn it into FileDescriptor
and use corresponding version of MediaRecorder.setOutputFile
.
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
if (resultData != null) {
URI outputFileUri = resultData.getData();
FileDesriptor outputFileDescriptor = getContentResolver().openFileDescriptor(outputFileUri, "w").getFileDescriptor()
mMediaRecorder.setOutputFile(outputFileDescriptor);
mMediaRecorder.start();
}
}
}
This method should be implemented inside some Activity
-class.
Room for improvement
So a user has to click Save
in system menu each time they starts new video. It would be nice to restore pre-KitKat state when my application could decide the directory on its own. Considering my experience if using “professional” applications this indeed is possible. E.g. “ES Explorer” asks user to explicitly give permission for operations on SD card only once (using the alike system dialog).