Feeding data from memory to MediaPlayer - android

Scenario:
Have encrypted mp3 files in my .apk. Need to decrypt and send to MediaPlayer object.
Problem:
After I read the files and decrypt them, how do I get MediaPlayer to play them ?
Now. MediaPlayer has 4 versions of setDataSource().
setDataSource(String path)
setDataSource(FileDescriptor fd)
setDataSource(FileDescriptor fd, long offset, long length)
setDataSource(Context context, Uri uri)
None of which are ideal for the situation. Guess ideal would be to give MediaPlayer an InputStream ?
Possible solutions:
Write decrypted data to file play
that file. A lot of IO overhead.
Create a dummy http server
(ServerSocket ?) and pass the url to
MediaPlayer. Again, messy. Am I
even allowed to create a socket.
Does anyone have a better solution ?

byte[] callData = ...;
String base64EncodedString = Base64.encodeToString(callData, Base64.DEFAULT);
try
{
String url = "data:audio/amr;base64,"+base64EncodedString;
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
mediaPlayer.start();
}
catch(Exception ex){
...
}

If you don't need all the functionality in MediaPlayer, I recommend trying AudioTrack. It's meant for basically what you describe. Unfortunately, MediaPlayer doesn't take an AudioTrack in its constructor, so the best solution in that case is to include a dummy Http server that sends your data back from a URL (which is what the Android 1.0 release notes recommends).

I'm not a 100% sure, but I don't think you have any other option than to temporarily save the the decrypted file before playing it.
This question is kind of similar, but I don't think you use the easy solution suggested there since you have an encrypted file. There is also provided a link to a tutorial for Custom Audio Streaming with MediaPlayer, but it seems like their solution also use a temporary file.

Related

Decrypting audio/video on-the-fly to MediaPlayer

I have been looking around for few days, but unable to get a clear answer on how to do it.
I have some encrypted audio/video files on my sdcard, which I would like to play which it will send to MediaPlayer, and start playing while decryption is working on the same time. It kinds of creates a buffer initially, and when it has enough initial data to start playing, MediaPlayer will start playing, and on background decrypting and keep sending the data over.
I looked around and most of the solution seem to point to having a localhttpserver, and then send the file to the server, and when it comes back, it will start playing the audio/video like a live stream. What I don't know is where does the decryption code comes in? In the server side? If so, in which part of it?
I have also seen this http://libeasy.alwaysdata.net/ solution, that possibly able to solve it, but I can't seem to understand how that works. I know that it creates a localserver, but on the Cipher part, how does my own decryption comes in play?
private void myPlay(String path) {
mServer = new LocalSingleHttpServer();
mServer.setCipher(myGetCipher());
mServer.start();
path = mServer.getURL(path);
mVideoView.setVideoPath(path);
mVideoView.start();
}
public void onCompletion(MediaPlayer mp) { // MediaPlayer.OnCompletionListener interface
mServer.stop();
}
As I am quite new to Android development, so do bear with me if I am unclear or anything.
Thank you.
The decryption is done by the http server, if you provide it a Cipher.
Code something like this (you will have to add the exception catching somewhere):
import javax.crypto.Cipher;
private Cipher myGetCipher() {
Cipher c = Cipher.getInstance("RC4");
c.init(Cipher.DECRYPT_MODE, new SecretKeySpec("myPassword".getBytes(), "RC4"));
return c
}

Using MediaRecorder to write to a buffer or FIFO

I am developing a low data rate VoIP kind of project . I need to capture audio at low data rates and store it in an internal buffer or FIFO (NOT in a file).
I would like to use low data rate .AMR encoders, which means AudioRecord is out. MediaRecorder looks like it does exactly what I want except that it seems to write to a file.
MediaRecorder takes a FileDescriptor... is there any way I can write a class that implements the FileDescriptor interface... acting as a sync for bytes... but instead of sending them to a file they are stored in a buffer? The documentation on FileDescriptor specifically says that Applications shouldn't write their own but why not and is it possible anyway?
http://docs.oracle.com/javase/1.4.2/docs/api/java/io/FileDescriptor.html
In short, I'd like to develop my own stream, and trick MediaRecorder to send data to it. Perhaps doing something tricky with opening both ends of a socket within the same APK and giving MediaRecorder the socket to write to? Using the socket as my FIFO? I'm somewhat new to this so any help/suggestions greatly appreciated.
I have a related question on the RX side. I'd like to have a buffer/fifo that feeds MediaPlayer. Can I trick MediaPlayer to accept data from a buffer fed by my own proprietary stream?
I know its a bit late to answer this question now...
...But if it helps here's the solution.
Android MediaRecorder's method setOutputFile() accepts FileDescriptor as a parameter.
As for your need a unix data pipe could be created and its FD could be passed as an argument in the following manner...
mediaRecorder.setOutputFile(getPipeFD());
FileDescriptor getPipeFD()
{
final String FUNCTION = "getPipeFD";
FileDescriptor outputPipe = null;
try
{
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
outputPipe = pipe[1].getFileDescriptor();
}
catch(Exception e)
{
Log.e(TAG, FUNCTION + " : " + e.getMessage());
}
return outputPipe;
}
The ParcelFileDescriptor.createPipe() creates a Unix Data Pipe and returns an array of ParcelFileDescriptors. The first object refers to the read channel (Source Channel) and the second one refers to the write channel (Sink Channel) of the pipe. Use MediaRecorder object to write the recorded data to the write channel...
As far as MediaPlayer is concerned the same technique could be used by passing the FileDescriptor object related to the created pipe's read channel to the setDataSource() method...

Can one retrieve data from a MediaPlayer's stream?

If I were to stream some sort of media to a MediaPlayer, is there any way I could copy it before/as/after it is played? For instance, if I were to stream a YouTube clip, is it possible to save that clip as it is being played?
Edit:
(Ocelot's answer made me realise how localised this question is).
What I am looking to do is copy the stream of a MediaPlayer already in progress (be it youtube or music stream). I want to be able to be notified when a new stream starts and ends. So far the only thing I found (for the latter) that is even remotely close it the broadcast string ACTION_AUDIO_BECOMING_NOISY but that doesn't really do anything for what I need. I there any way to do this?
I haven't tested this, and it looks like quite a bit of work, but here is what I would try:
Create a subclass of Socket. In this class, you can handle all byte reads, and save the stream locally or do whatever you want with it
Create your own content provider, which you can use to pass URIs to your media player, in your own format. Example: mystream://youtube.com/watch?v=3Rhy37u
In your content provider, override the openFile method and in it, open your own socket, and create a ParcelFileDescriptor with it.
Now, simply passing the new format url to your mediaplayer should make all streams go through your Socket, where you can save your data.
one way is to first find out where the video is in the server for exmaple in youtube with simple regex like this :
Regex("(?<=&t=)[^&]*").Match(file).Value;
you could retrieve url to the video and then download it like
public static void Download(string videoID, string newFilePath)
{
WebClient wc = new WebClient();
string file = wc.DownloadString(string.Format("http://www.youtube.com/watch?v={0}", videoID));
string t = new Regex("(?<=&t=)[^&]*").Match(file).Value;
wc.DownloadFile(string.Format("http://www.youtube.com/get_video?t={0}=&video_id={1}",t,videoID), newFilePath);
}
it's c# code but you could easily convert it to java.
#zrgiu
I tried to go with this solution, but the MediaPlayer retrieves a FileDescriptor from the URI, so sadly no http URL can be passed like this.
I also found another solution, it suggests to create a local ProxyServer on your device to serve files from the internet, it should be possible to also save the files streamed via the proxy.

Android Assets-Folder: it takes always the first file by alphabetical order

I'm new in Java/Android programming, so please have patience with me.
I try to play a mp3 which is locate und the assets folder. I know there is another way with the /res/raw/ folder, but use the assets-folder because later I'll try to access the file by String.
This code works to play a mp3-file:
try
{
MediaPlayer mp = new MediaPlayer();
FileDescriptor sfd = getAssets().openFd("song.mp3").getFileDescriptor();
mp.setDataSource(sfd);
mp.prepare();
mp.start();
}
catch(Exception e) {}
Now the problem: In the same assets-folder is another mp3 file stored. Though I specify the name of the mp3 to use it take the one which comes first in alphabet. E.g. the other file is named "music.mp3" it plays this one. Renaming it to "worldmusic.mp3" it will play "song.mp3". Rerename "worldmusic.mp3" back to "music.mp3" it will take this mp3 again. Another test: Renaming "song.mp3" to something other so the application can find whats specify by the code above will result that no song is played. So this means the songname have to exist, although it take arbitrary the song first in alphabet.
I'm testing with the AVD emulator of eclipse. But I think the behaviour would be the same on a real device.
Have someone an idea about this problem?
I don't believe using FileDescriptor is the right way to do this. Try using .create() and a Uri instead:
MediaPlayer mp = MediaPlayer.create(getBaseContext(), songUri);
mp.start();
Not sure why, but the URI syntax doesn't seem to work for the assets. Try getting the AssetFileDescriptor instead, as answered in the related question:
Play audio file from the assets directory

MediaPlayer cant play audio files from program data folder?

When i record my audio from MIC and store file in /data/data/..... why
MediaPlayer can't play this file ? If i change destination to /
sdcard/..... - all works great. I do something wrong ? I not found
limitation for MediaPlayer. Device - Samsung T959 (Galaxy S)
Thanks, i hope anybody know solution....
i had the same issues... whenever i used setDataSource with filepaths, it would not work; kept getting IOException.
changing my code to use setDataSource(FileDescriptor) does work... and with this i don't have to copy the files to SDCard or anything like that.
So, with a simple File object, create a new FileInputStream and pass the actual file descriptor as data source as in:
setDataSource((new FileInputStream(myFileInstance)).getFD());
creating an input stream pulls the file data into memory and thereby addresses /data/data access violations.
Better practice is to close the stream, like this:
FileInputStream stream = new FileInputStream(path);
mediaPlayer.setDataSource(stream.getFD());
stream.close();
Even better from Kotlin:
mediaPlayer.setDataSource(
FileInputStream(path).use { it.fd }
)
It is noted in the documentation of the method:
android.media.MediaPlayer.setDataSource(FileDescriptor fd)
Sets the data source (FileDescriptor) to use. It is the caller's
responsibility to close the file descriptor. It is safe to do so as
soon as this call returns.
use MediaPlayer.setDataSource(FileDescriptor fd)
Try setting ContentValues and to store some standard meta-data properties. Then using a ContentResolver to set the meta-data and Uri to the file.
see: http://developer.android.com/guide/topics/media/index.html
"Example: Audio Capture Setup and Start", then try changing Uri base to your /data/data/-filename.
Due to the Android security model, MediaPlayer haven't enough rights. It can access SD card, but can't access another places with out permissions.
As so, setDataSource(...) can thrown SecurityException and I think it's happening.
You can play this file next ways:
copy it to temp dir and play;
copy it to temp dir and play;
copy it to sdcard;
read it fully to memory and try play via stream.

Categories

Resources