I have a encrypted mp4 file about 130M that I want to play in a VideoView. What is the best way to stream it? I don't want to copy to an unencrypted file and then use that is the time it takes to decrypt it is prohibitive on slower devices. What I have so far is copying to a file and start the VideoView by giving it that buffered file before the decryption finishes.
public class DecryptVideoTask extends AsyncTask<Void, File, File> {
public static final String TAG = DecryptVideoTask.class.getCanonicalName();
public static final int READ_AHEAD_BYTES = 65469100;
private Context context;
private VideoView videoView;
private String encryptedFilePath;
private ProgressDialog progressDialog;
/**
*
* @param context currentContext
* @param videoView VideoView that will play the video
* @param encryptedFilePath conceal encrypted video
* @param progressDialog progressDialog to hide when the video starts
*/
public DecryptVideoTask(Context context, VideoView videoView, String encryptedFilePath, ProgressDialog progressDialog) {
this.context = context;
this.videoView = videoView;
this.encryptedFilePath = encryptedFilePath;
this.progressDialog = progressDialog;
}
@Override
protected File doInBackground(Void... params) {
File bufferFile = null;
try {
bufferFile = File.createTempFile("temp", ".mp4", context.getCacheDir());
BufferedOutputStream bufferOS = new BufferedOutputStream(
new FileOutputStream(bufferFile));
// getting CipherInputStream. encryptedFilePath is the path to a conceal encrypted file
InputStream is = Application.getInstance().getCryptoManager().decrypt(new File(encryptedFilePath));
BufferedInputStream bis = new BufferedInputStream(is, 524288);
byte[] buffer = new byte[32768];
int numRead;
long totalRead = 0;
boolean started = false;
long startTime = System.currentTimeMillis();
while ((numRead = bis.read(buffer)) != -1) {
bufferOS.write(buffer, 0, numRead);
bufferOS.flush();
totalRead += numRead;
Log.d(TAG, "total read: " + totalRead);
if (totalRead > READ_AHEAD_BYTES && !started) {
Log.d(TAG, "decryption took: " + (System.currentTimeMillis() - startTime) + "ms");
Log.d(TAG, "starting video: " + encryptedFilePath);
publishProgress(bufferFile);
started = true;
}
}
bufferOS.close();
bis.close();
if (!started) {
Log.d(TAG, "file is decrypted but video has not been started, starting now: " + encryptedFilePath);
publishProgress(bufferFile);
started = true;
}
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
return bufferFile;
}
return bufferFile;
}
@Override
protected void onProgressUpdate(File... values) {
videoView.setVideoURI(Uri.parse("file://" + values[0].getAbsolutePath()));
videoView.requestFocus();
videoView.start();
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
}
The video starts but depending on what buffer sizes I fiddle with (READ_AHEAD_BYTES) the video may error out after a few seconds with
01-23 09:54:13.219 12087-12099/com.github.browep.privatephotovault E/MediaPlayer﹕ error (1, -1004)
01-23 09:54:13.219 12087-12087/com.github.browep.privatephotovault E/MediaPlayer﹕ Error (1,-1004)
and a popup that says "can't play this video". Is there a better way to do this? FileDescriptor? something?