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.
Related
I thought I found a way to do this, but it seems like this is for exoplayer1 (the first answer to this question): Extracting metadata from Icecast stream using Exoplayer
I already have a way to read Icy metadata, but it uses a stream of it's own, so it creates extra data costs for the user.
How can this be done just using one instance of the stream?
This can be done easily by registering a listener for your ExoPlayer instance:
exoPlayer.addListener(new Player.Listener() {
#Override
public void onMediaMetadataChanged(MediaMetadata mediaMetadata) {
String title = (String)mediaMetadata.title; // This is the artist / song
}
}
According to this post, the song's artist, album, track can be retrieved from a BroadcastReceiver, but the song's information which are current playing in Spotify or Pandora cannot be retrieved.(Maybe because these apps don't send broadcast while playing songs).
My question is, how to get current playing song's information on system level, no matter whether the app is broadcasting.
Listening for com.android.music.* broadcasts is the official way to do this on Android.
Many apps like lyrics displayers rely on it to get info on the current track.
Sadly, if the app is not broadcasting the solution is either to have a look at their Dev documentation in case they provide another way to do this or directly ask them to implement it.
For Spotify, it looks like right now they use a 'com.spotify.music.metadatachanged' broadcast (and a settings in the app). You will have to track down separate cases like this one, nothing forces Android devs to implement the standard broadcast.
In android 4.4+, you can use the RemoteController class to get track information from the current playing song. This works with every app that implements a RemoteControlClient. It also works with video- and streamservices like chromecast.
http://forum.xda-developers.com/showthread.php?p=48702254#post48702254
This is a very handy guide if you want to use this RemoteController.
And the developers page:
https://developer.android.com/reference/android/media/RemoteController.html
definitely u know the path of the song where is running
Uri myUri1 = Uri.parse(path);
now u can call this method having the path then we can get all information either in the activity or from the service by binding
private void getTrackInfo(Uri audioFileUri) {
MediaMetadataRetriever metaRetriever= new MediaMetadataRetriever();
metaRetriever.setDataSource(getRealPathFromURI(audioFileUri));
String artist = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
String title = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
}
private String getRealPathFromURI(Uri uri) {
File myFile = new File(uri.getPath().toString());
String s = myFile.getAbsolutePath();
return s;
}
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...
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.
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.