AES/ECB/PKCS5Padding encrypted video with exoplayer android (JAVA/KOTLIN) - java

I am trying to download a video file and encrypt it while downloading then play it using Exoplayer when I search for how to play AES Video I found I must make my own DataSource. then I try to implement it in many ways but always it gives me that error. (The project is old you might see java and kotlin code together)
Error
ExoPlayerImplInternal: Playback error
com.google.android.exoplayer2.ExoPlaybackException: Source error
at com.google.android.exoplayer2.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:632)
at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:602)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loopOnce(Looper.java:233)
at android.os.Looper.loop(Looper.java:344)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: com.google.android.exoplayer2.source.UnrecognizedInputFormatException: None of the available extractors (FlvExtractor, FlacExtractor, WavExtractor, FragmentedMp4Extractor, Mp4Extractor, AmrExtractor, PsExtractor, OggExtractor, TsExtractor, MatroskaExtractor, AdtsExtractor, Ac3Extractor, Ac4Extractor, Mp3Extractor, AviExtractor, JpegExtractor) could read the stream.
at com.google.android.exoplayer2.source.BundledExtractorsAdapter.init(BundledExtractorsAdapter.java:92)
at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1017)
at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:412)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
DataSource
Reading the Chipher inputStream and decrypt it
class EncryptedDataSource(private val key: String) : DataSource {
private var inputStream: CipherInputStream? = null
private lateinit var uri: Uri
override fun addTransferListener(transferListener: TransferListener) {}
override fun open(dataSpec: DataSpec): Long {
uri = dataSpec.uri
try {
val file = File(uri.path)
val skeySpec = SecretKeySpec("e9wF6xA6WpTfyjeVrqOnesZD/KGrUDdx".toByteArray(), KeyProperties.KEY_ALGORITHM_AES)
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, skeySpec)
inputStream = CipherInputStream(file.inputStream(), cipher)
if (dataSpec.position != 0L) {
inputStream?.forceSkip(dataSpec.position) // Needed for skipping
}
} catch (e: Exception) {
}
return dataSpec.length
}
#Throws(IOException::class)
override fun read(buffer: ByteArray, offset: Int, readLength: Int): Int =
if (readLength == 0) {
0
} else {
inputStream?.read(buffer, offset, readLength) ?: 0
Log.i("HeshamNyla",inputStream.toString());
}
override fun getUri(): Uri? =
uri
#Throws(IOException::class)
override fun close() {
inputStream?.close()
}
}
Factory
class EncryptedDataSourceFactory(
private val key: String
) : DataSource.Factory {
override fun createDataSource(): EncryptedDataSource =
EncryptedDataSource(key)
}
Save the bytes
encrypt and save the downloaded bytes
private void downloadFile(ResponseBody body,Message msg,boolean type) {
try {
int count;
byte data[] = new byte[1024 * 4];
long fileSize = body.contentLength()+length;
InputStream bis = new BufferedInputStream(body.byteStream(), 1024 * 8);
File outputFile = new File(new LocalVideos(getApplicationContext()).getFolder()+name);
OutputStream output = new FileOutputStream(outputFile,type);
Cipher encipher = Cipher.getInstance("AES/CBC/PKCS5PAdding");
SecretKey skey = new SecretKeySpec("e9wF6xA6WpTfyjeVrqOnesZD/KGrUDdx".getBytes(), KeyProperties.KEY_ALGORITHM_AES);
//Lgo
encipher.init(Cipher.ENCRYPT_MODE, skey);
CipherInputStream cis = new CipherInputStream(bis, encipher);
CipherOutputStream cos = new CipherOutputStream(output,encipher);
long total = length;
long startTime = System.currentTimeMillis();
int timeCount = 1;
while ((count = cis.read(data)) != -1) {
total += count;
totalFileSize = (int) ((int) (fileSize / (Math.pow(1024, 2))));
double current = Math.round(total / (Math.pow(1024, 2)));
int progress = (int) ((int) ((total * 100) / fileSize));
long currentTime = System.currentTimeMillis() - startTime;
Download download = new Download();
download.setTotalFileSize(totalFileSize);
if (currentTime > 1000 * timeCount) {
download.setCurrentFileSize((int) current);
download.setProgress(progress);
sendNotification(download);
timeCount++;
}
cos.write(data, 0, count);
}
onDownloadComplete(msg);
cos.flush();
cos.close();
bis.close();
}catch (Throwable exception){
stopForeground(true);
stopSelf(msg.arg1);
notificationManager.cancel(NOTFIYID);
notificationBuilder.setProgress(0,0,false);
notificationBuilder.setContentText("Failed try again");
notificationManager.notify(NOTFIYID, notificationBuilder.build());
new VideosState(getApplicationContext()).updateVideoState(name,VideosState.STATE_STOPED);
}
}
use dataSource
use dataSource Factory
mediaItem = new MediaItem.Builder()
.setUri(Uri.fromFile(new File(url)))
.build();
DataSource.Factory dataSource= new EncryptedDataSourceFactory("");
MediaSource videoSource = new ProgressiveMediaSource.Factory(dataSource)
.createMediaSource(mediaItem);
playerView.setPlayer(player);
playerView.setKeepScreenOn(true);
player.setPlaybackParameters(parameters);
player.setMediaSource(videoSource);
player.prepare();

Related

How to scroll in video spring boot?

I have built a dummy video streaming application in spring boot,I have noticed that i cant skip to another frame or to a certain time in a video.
Here is my code to read videoFile
public byte[] getVideo() throws IOException {
byte[] bytes = new byte[(int) file.length()];
FileInputStream inputStream = new FileInputStream(file);
inputStream.read(bytes);
return bytes;
}
and this is my what my video controller returns
return ResponseEntity.status(HttpStatus.OK)
.header("Content-Type","video/mp4")
.header("Content-length",String.valueOf(streamingService.file.length()))
.body(streamingService.getVideo());
note i am not using any frontend
So after some experimentation, I found that the browser (Chrome) made another GET request to a certain byte range, and my getVideo() method was not well-written enough to accept the byte range and return the required byte range.
After rewriting my VideoStreamingService, the problem was solved for Chrome.
Here is my rewritten code for getVideo():
byte[] data;
Long fileSize = file.length();
String[] ranges = range.split("-");
rangeStart = Long.parseLong(ranges[0].substring(6));
if (ranges.length > 1) {
rangeEnd = Long.parseLong(ranges[1]);
} else {
rangeEnd = fileSize - 1;
}
if (fileSize < rangeEnd) {
rangeEnd = fileSize - 1;
}
contentLength = String.valueOf((rangeEnd - rangeStart) + 1);
data = readByteRange( rangeStart, rangeEnd);
return data;
readRangeByte() method so I can read data appropriately:
public byte[] readByteRange(long start, long end) throws IOException {
try (InputStream inputStream = (Files.newInputStream(Path.of(videoFileName)));
ByteArrayOutputStream bufferedOutputStream = new ByteArrayOutputStream()) {
byte[] data = new byte[128];
int nRead;
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
bufferedOutputStream.write(data, 0, nRead);
}
bufferedOutputStream.flush();
byte[] result = new byte[(int) (end - start) + 1];
System.arraycopy(bufferedOutputStream.toByteArray(), (int) start, result, 0, result.length);
return result;
}
}
and my controller class code:
public ResponseEntity<byte[]> video(#RequestHeader(value = "Range",required = false) String range) throws IOException {
if (range == null) {
return ResponseEntity.status(HttpStatus.OK)
.header("Content-Type", "video/mp4")
.header("Content-Length", String.valueOf(streamingService.file.length()))
.body(streamingService.readByteRange(streamingService.getRangeStart(),streamingService.file.length()-1));
}
byte[] data = streamingService.getVideo(range);
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
.header("Content-Type", "video/mp4")
.header("Accept-Ranges", "bytes")
.header("Content-Length", streamingService.getContentLength())
.header("Content-Range", "bytes" + " " + streamingService.getRangeStart() + "-" + streamingService.getRangeEnd() + "/" + streamingService.file.length())
.body(data);
After #Andreas suggestion the code works very well with both the browsers

Android - Audio Clipping when recording audio (crest/peak clipping and periodic 0 bit values in between)

I am trying to record an audio stream via a Bluetooth device. I am using Bluetooth SCO for getting Bluetooth audio and AudioRecord class to record audio.
I am recording RAW .PCM files with MONO Channel with a sampling rate of 16000
I am calculating BufferSize like this
private static final int BUFFER_SIZE_FACTOR = 2;
private static final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLING_RATE_IN_HZ,CHANNEL_CONFIG, AUDIO_FORMAT) * BUFFER_SIZE_FACTOR;
This is how I am getting/writing audio currently,
private class RecordingRunnable implements Runnable {
#Override
public void run() {
setFileNameAndPath();
final ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
try (final FileOutputStream outStream = new FileOutputStream(mFilePath)) {
while (recordingInProgress.get()) {
int result = recorder.read(buffer, BUFFER_SIZE);
if (result < 0) {
throw new RuntimeException("Reading of audio buffer failed: " +
getBufferReadFailureReason(result));
}
outStream.write(buffer.array(), 0, BUFFER_SIZE);
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Writing of recorded audio failed", e);
}
}
I did a little research and found that the clipping effect could be because of the wrong Byte order (LITTLE_ENDIAN or BIG_ENDIAN) or Because of poor multithreading. However in this current implementation, I am not able to understand how bytes are being ordered and saved & what can I do to fix the clipping/noise problem.
I am starting my recorder runnable like this
recordingThread = new Thread(new RecordingRunnable(), "Recording Thread");
recordingThread.start();
recordingThread.setPriority(Thread.MAX_PRIORITY);
I got same issue and I resolved this problem with below code.
private byte[] short2byte(short[] sData, int size) {
int shortArrsize = size;
byte[] bytes = new byte[shortArrsize * 2];
for (int i = 0; i < shortArrsize; i++) {
bytes[i * 2] = (byte) (sData[i] & 0x00FF);
bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
sData[i] = 0;
}
return bytes;
}
......
int bufferSize = AudioRecord.getMinBufferSize(48000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
short[] buffer = new short[bufferSize];
int source = MediaRecorder.AudioSource.VOICE_RECOGNITION;
mAudioRecorder = new AudioRecord(source, 48000,
AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
int state = mAudioRecorder.getState();
if (state != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "Can not support");
return;
}
mAudioRecorder.startRecording();
while (mIsRecording) {
int bufferReadResult = mAudioRecorder.read(buffer, 0, bufferSize);
if (bufferReadResult < 0) {
continue;
}
try {
byte data[] = short2byte(buffer, bufferReadResult);
fos.write(data, 0, bufferReadResult * 2);
} catch (IOException e) {
e.printStackTrace();
}
}

Video Decryption Issue In Android

I am using "AES" algorithm for Decryption of video, Which is stored on External Sd card in Encrypted format.While decrypting video its decrypt only 47% and get stop. please give solution
public void createDecryptedFile(File decryptedFileDir, File decryptedFile,
File encryptedFile) {
try {
if (!decryptedFileDir.exists()) {
decryptedFileDir.mkdirs();
}
Cipher decipher;
decryptedFile.createNewFile();
deleteFile = decryptedFile;
FileInputStream encryptedFileInputstream = new FileInputStream(
encryptedFile);
FileOutputStream decryptedFileOutputstream = new FileOutputStream(
decryptedFile);
decipher = Cipher.getInstance("AES");
Key key = generateKey();
decipher.init(Cipher.DECRYPT_MODE, key);
CipherOutputStream cos = new CipherOutputStream(
decryptedFileOutputstream, decipher);
byte data[] = new byte[10000 * 1024];
int count;
try {
while ((count = encryptedFileInputstream.read(data)) != -1 && !stopConversion) {
Log.d("#########", "##########");
total += count;
Log.e("convert count", total + "");
cos.write(data, 0, count);
final long l = encryptedFile.length();
runOnUiThread(new Runnable() {
public void run() {
// Show percentage
loadingpercent.setText("" + (int) (total * 100 / l) + "%");
}
});
Log.d("$$$$$$$$",""+encryptedFileInputstream.read(data));
}
Replace :-
CipherInputStream cis = new CipherInputStream(encryptedFileInputstream, cipher);
int count;
try {
int b;
byte[] d = new byte[10000 * 2048];
while ((b = cis.read(d)) != -1 && !stopConversion) {
total += b;
final long l = encryptedFile.length();
decryptedFileOutputstream.write(d, 0, b);
runOnUiThread(new Runnable() {
public void run() {
loadingpercent.setText("" + (int) (total * 100 / l) + "%");
}
});
}

Decode mp3 to pcm, and play with audiotrack in Google Android

First of all, if not using function decode_path , I can play .wav file with my code , and it works fine I use Jlayer and audio track to play the song.
Second, if I use function decode_path it can decode mp3 to pcm file , and pass the byte[] to function PlayAudioTrack, and let it play.
The quesion is,I don't know where my code is wrong , I use 320Kbps, 44.1Khz stereo type, Layer3 mp3, but the AudioTrack plays noise but no music~!!!!
can anyone ?
???
My code
public void PlayAudioTrack(String filePath) throws IOException{
int intSize = android.media.AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT, intSize, AudioTrack.MODE_STREAM);
//Reading the file..
int count = 512 * 1024; // 512 kb
// byte[] byteData = null;
// byteData = new byte[(int)count];
//we can decode correct byte data here
byte[] byteData = null;
byteData = decode_path(filePath, 0, 20000);
File file = null;
file = new File(filePath);
FileInputStream in = null;
try {
in = new FileInputStream( file );
} catch (FileNotFoundException e) {
e.printStackTrace();
}
int bytesread = 0, ret = 0;
int size = (int) file.length();
at.play();
while (bytesread < size) {
Log.e("devon","write byte array with sizes");
ret = in.read( byteData,0, count);
if (ret != -1) {
Log.e("devon","Write the byte array to the track");
at.write(byteData,0, ret);
bytesread += ret;
}else break;
}
at.stop();
at.release();
}
public static byte[] decode_path(String path, int startMs, int maxMs)
throws IOException{
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
float totalMs = 0;
boolean seeking = true;
File file = new File(path);
InputStream inputStream = new BufferedInputStream(new FileInputStream(file), 8 * 1024);
try {
Bitstream bitstream = new Bitstream(inputStream);
Decoder decoder = new Decoder();
boolean done = false;
while (! done) {
Header frameHeader = bitstream.readFrame();
if (frameHeader == null) {
done = true;
} else {
totalMs += frameHeader.ms_per_frame();
if (totalMs >= startMs) {
seeking = false;
}
if (! seeking) {
SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);
if (output.getSampleFrequency() != 44100
|| output.getChannelCount() != 2) {
throw new IllegalArgumentException("mono or non-44100 MP3 not supported");
}
short[] pcm = output.getBuffer();
for (short s : pcm) {
outStream.write(s & 0xff);
outStream.write((s >> 8 ) & 0xff);
}
}
if (totalMs >= (startMs + maxMs)) {
done = true;
}
}
bitstream.closeFrame();
}
return outStream.toByteArray();
} catch (BitstreamException e) {
throw new IOException("Bitstream error: " + e);
} catch (DecoderException e) {
Log.w(TAG, "Decoder error", e);
throw new IOException("Decoder error: " + e);
}
}
public void PlayAudioTrack(String filePath) throws IOException{
int intSize = android.media.AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT, intSize, AudioTrack.MODE_STREAM);
//Reading the file..
int count = 512 * 1024; // 512 kb
// byte[] byteData = null;
// byteData = new byte[(int)count];
//we can decode correct byte data here
byte[] byteData = null;
byteData = decode_path(filePath, 0, 20000);
int temp =0;
at.play();
while (temp<byteData.length)
{
at.write(byteData, temp, count);
temp+= count;
}
at.stop();
at.release();
}

How to create a zip archive containing google cloud storage objects within appengine java app?

Let's say I have 50 objects (15Mb each) stored in Google Cloud Storage. Now I need to create a zip archive containing all of them and store the resultant file back at GCS.
How can I do that from within an appengine java app?
I wrote the method below which seems to be working fine.
public static void zipFiles(final GcsFilename targetZipFile,
final GcsFilename... filesToZip) throws IOException {
Preconditions.checkArgument(targetZipFile != null);
Preconditions.checkArgument(filesToZip != null);
Preconditions.checkArgument(filesToZip.length > 0);
final int fetchSize = 4 * 1024 * 1024;
final int readSize = 2 * 1024 * 1024;
GcsOutputChannel outputChannel = null;
ZipOutputStream zip = null;
try {
GcsFileOptions options = new GcsFileOptions.Builder().mimeType(MediaType.ZIP.toString()).build();
outputChannel = GCS_SERVICE.createOrReplace(targetZipFile, options);
zip = new ZipOutputStream(Channels.newOutputStream(outputChannel));
GcsInputChannel readChannel = null;
for (GcsFilename file : filesToZip) {
try {
final GcsFileMetadata meta = GCS_SERVICE.getMetadata(file);
if (meta == null) {
LOGGER.warning(file.toString() + " NOT FOUND. Skipping.");
continue;
}
//int fileSize = (int) meta.getLength();
// LOGGER.fine("adding " + file.toString());
ZipEntry entry = new ZipEntry(file.getObjectName());
zip.putNextEntry(entry);
readChannel = GCS_SERVICE.openPrefetchingReadChannel(file, 0, fetchSize);
final ByteBuffer buffer = ByteBuffer.allocate(readSize);
int bytesRead = 0;
while (bytesRead >= 0) {
bytesRead = readChannel.read(buffer);
buffer.flip();
zip.write(buffer.array(), buffer.position(), buffer.limit());
buffer.rewind();
buffer.limit(buffer.capacity());
}
} finally {
zip.closeEntry();
readChannel.close();
}
}
} finally {
zip.flush();
zip.close();
outputChannel.close();
}
}

Categories

Resources