Android - Play a song that is still downloading - java

I'm creating an app in which multiple devices can connect to each other in a LAN and play songs from each other's device.
Right now, when device A requests a song from device B, B sends the song file back to A over a socket. Once the song finishes downloading, A starts playing it using MediaPlayer.
Now the problem with this is that even on a high speed LAN, it can take a couple of seconds to finish fetching the song, so there's a noticeable delay between selecting a song and it's playback actually starting.
I want to find a way to play a song while it's still being downloaded. Here are some possible solutions I found, but wasn't able to explore for certain reasons:
In the MediaPlayer documentation, I noticed a constructor that takes a MediaDataSource implementation allowing me to manually return parts of the media file whenever the MediaPlayer requires it so I could hold off returning a byte until it finishes downloading (if it hasn't finished downloading already), effectively acting like a "buffering" action. It seemed like a solution but unfortunately, my app's minSdk is set to 16 and MediaDataSource is for 23 and above only, so I couldn't try it out.
One option was to use MediaPlayer's setDataSource(FileDescriptor fd, long offset, long length) method. This would allow me to tell the MediaPlayer to only play up to a certain byte of the song. That way, I could wait for more parts of the file (or the entire file) to become available, and then use setNextMediaPlayer() and pass in a new MediaPlayer object that prepares the entire song and is made to seek up to the point where the previous media player object will stop playing so that there's a seamless transition.
But there's another problem with this. I need to be able to calculate the millisecond position that would be reached at that last specified byte of the first incomplete media player object. Otherwise I wouldn't know what position to seek the next media player object to in order to get a seamless transition. This calculation seems impossible for lossy formats.
I don't really know if this option will work or not, I'm just making assumptions. I noticed that setDataSource() takes a Uri. If that Uri points to a file on the phone, the media player just loads the entire file. But if the Uri points to an audio file on the internet that needs to be streamed, it figures that out on it's own and it handles all the details of downloading and buffering. So what I want to know is, is it possible to expose the song on device B as a Uri to device A so that media player just treats it as if it's a song on the internet? All this time I was using sockets and manually copying a file from one device to another so I have no idea how this would work. Could anyone explain if this is possible?
There's actually a reason why I haven't been exploring ways to "stream" a song from one device to another. That's because I want to song to be cached on device B so that later if I switch to another song and then back the previously played song from device A, it shouldn't have to stream it again. It should just play the cached copy.
Finally, I came across ExoPlayer. It seems to provide a large amount of customization. It seems like I could make custom implementations of it's classes to handle all the buffering. But the documentation and examples are few and far too complicated for me. I have no idea how to pull it off. Is this solution too complex for what I want to achieve?
Hope someone can point me in the right direction here.

Ended up using an answer from here:
MediaPlayer stutters at start of mp3 playback
This solution sets up a local server that the MediaPlayer can use to stream the file. The server only sends bytes to the MediaPlayer while those bytes are available. Otherwise, it blocks until more bytes are available. Thus the MediaPlayer buffers it as if it were a file from a web server.
I took the same class provided in that answer and just tweaked it to fit my situation.

Related

How can I implement automatic loading for JSON object in android?

I am working on an audio stream android app and I parsed JSON object from a server to a TextView to display 'now Playing' for the song name and artist. So when the play button is clicked, the song name playing artist is displayed to the user. The problem is that I want this automatically loaded to the app view when JSON URL link is updated from the server. I don't want the user pressing pause and play to update the view from the app. How do I go about this because I don't want the user restarting the service each time a new song isPlaying to get song information.
You can either poll server in short intervals to check if song changed or open socket connection to server to make possible server initiating communication to device.
First approach in simplest form is a very bad practice, as it puts strain on both device and server to check it often enough.
However there is different way to use it, called long polling. With this, you send request to the server, and server does not respond immediately, but holds connection open until it has something to say. After getting reply instantaneously new request is created to make sure no delay is made by it.
The best approach is opening a socket connection, but not every server and program support it.
You can try libraries like SignalR (this one is for .NET mostly, but it's the first one that came to my mind) that choose which approach is the best and takes care of holding connection, reconnecting etc.
Are you fetching this JSON metadata every time the song is played? If so, that doesn't sound like a good idea. The ideal would be to add song metadata when adding a song to the playlist, then either update it periodically (once a couple of days perhaps) and save that information into a SQLite database for later retrieval.

android audio become noisy response time

I am developing an App that plays back audio, using media player. The Problem is the response time from when the headphone are removed to the time the audio playback is paused. The same problem can be found in RandomMusicPlayer
I have followed instructions from Handling the AUDIO_BECOMING_NOISY Intent
from android website. I thank you for any help you provide.
Peace.
Thank You ssuukk, for your response. Between development and other obligations have delayed my response. I now have time to post the solution to my problem.
My audio track is a voice track oppose to music, and I was getting about three to four word out after removing the headphones. I implemented Handling the AUDIO_BECOMING_NOISY Intent from android website and RandomMusicPlayer, and the RandomMusicPlayer has the same problem. I did suspect, not implementing as an inner class and the need for a second intent was the problem.
I found my solution here
Peace.
You need to be more specific. I'm using AUDIO_BECOMING_NOISY in my application and it never was any lag between removing headphones and pausing, nor a single complaint from an user that player is pausing too late. So the problem must be somewhere in your code.

Android play and record radio stream simultaneously

I have Android application, which stream radio stream with m3u extension. It is playing this stream in MediaPlayer through method setDatasource(url). Of course, this works greaet because I don't need to take a care about reading inputstream, buffer that, repeatedly send to the MediaPlayer with seeking the last position and so on.
But now I need to stream and also record the stream. How should I do that? What are your recommendations?
Like use NanoHTTPD (but how, where should I start to study own webserver logic)
Or use SockerHandler somehow?
Or any other solution? I don't know even, where should I start to study this problem.
Many thanks

Since when is the phone charging/discharging

I wanted to learn more about the Android Services / Broadcasts, so I started a simple project, to create a battery monitoring app. It turned out pretty good, I'm using it for a few days now, but I want to add a new function: to show since when is the phone charging/discharging.
First I thought that I would create two static fields in my BoradcastReciever extension class, where I get and publish the data about the battery, one for the actual state (charging/discharging), and one for the time, when the change in state happened. This way, I could just subtract from the current time the last change, and know exactly since when is the phone charging/discharging.
But there is a problem with this solution: It won't show the correct data at first, when a user starts the app. I wouldn't make a big deal of it, but I saw that Android tracks this data somewhere, because inside my phone settings I found this information, so why take the hard way.
So my question is: is there an easy way to get from the Android system the date/time (no matter what format) of the last charging state change?
I looked at the BatteryManager reference but there are no constants named after what I seek, and which I could use, to get the information from the Intent of my receiver.
The Android OS tracks the connect/disconnect of a power source, but does not make this data accessible to apps. You have to record this all yourself, using intent filters.
The two intent filters to use are android.intent.action.ACTION_POWER_CONNECTED and android.intent.action.ACTION_POWER_DISCONNECTED; with these, you can monitor when the power source is connected and disconnected.
You can find information about this process explained incredibly clearly here. Another blog describing the process can be found here.

Android MediaPlayer - Sometimes No Video is Played Even Though Audio Plays

I am developing an Android App and I'm using the Android SDK's MediaPlayer to play some videos in my app. When I play the video in my app, about one out of five times, the audio plays without video. It's not a simple coding error because most of the time the video plays perfectly.
I have considered that a race condition in my code caused the bug. However, I added a number of debug statements and everything seems to be set up properly when the video does not play.
I have scanned the web and SO trying to find solutions but none have been adequate (see below).
Has anyone run into this type of problem before? If so, what did you do?
Similar Questions:
MediaPlayer Video not played
android media player shows audio but no video
android video, hear sound but no video
Some More Details:
I've come across this bug on two phones. On a Samsung Charge video plays 80% of the time and 20% of the time there's audio but no video. On a T-Mobile Comet it's much worse; video only plays about 10% of the time.
It's not a problem with the file, I've tried various video files and codecs and get the same issues.
It's not a problem with the storage medium. I've tried playing the video when it was stored on internal storage and the sdcard, neither makes a difference. I have even tried reading some of the file before playing it, hoping that the system will cache it, but that doesn't seem to help either.
Update:
I've been debugging this and looking through logcat. I've found that when the video works, something like the following appears in logcat:
09-28 00:09:03.651: VERBOSE/PVPlayer(10875): setVideoSurface(0x65638)
But when video doesn't play, it looks like there's a null entry:
09-28 00:03:35.284: VERBOSE/PVPlayer(10875): setVideoSurface(0x0)
Update 2:
When the video fails to play, the function MediaPlayer.OnInfoListener with parameters what==MEDIA_ERROR_UNKNOWN(0x1) and extra==35. I looked through the Android code-base to try to determine what unknown error 35 means. I came across the file pv_player_interface.h, which indicates that error code 35 corresponds to something called a PVMFInfoTrackDisable. I googled that term which brought me to a file called pvmf_return_codes.pdf. That file gave me the following unintelligible explanation:
4.34. PVMFInfoTrackDisable
Notification that paticular track is
disable. This one is on a per track basis. For uncompressed
audio/video formats, during the process of selecting tracks available
in content, if the decoder doesn't support the track, a
PVMFInfoTrackDisable event is sent. The event, if necessary, will be
sent once per track.
I feel like I've gone a long way, but am no closer to finding an answer... still investigating.
I solved the problem, albeit in a totally hackish way. There are two problems actually:
The Evil Info 35 Message: I found that occasionally MediaPlayer.OnInfoListener will get called with extra==35. When this happens, you're screwed and the video will not play properly. I have no idea what causes it. The only fix I found was to try restarting the video and going through the whole prepareAsync process over again. Video playback usually works the second time.
Video Size Not Set: Even after MediaPlayer.OnPreparedListener gets issued (or equivalently prepare() returns) the video size may not be been set. The video size will usually be set a couple of miliseconds after prepare returns, but for a few moments it is in a nebulous state. If you call MediaPlayer.start() before the video size is set, then playback will sometimes (but not always) fail. There are two potential solutions to this: (1) poll MediaPlayer.getVideoHeight() or getVideoWidth() until they're non-zero or (2) wait until OnVideoSizeChangedListener is called. Only after one of these two events, should you call start().
With these two fixes, video playback is much more consistent. It's very likely that these problems are bugs with my phones (Samsung Charge and a T-Mobile Comet) so I won't be surprised if there are different, but similar problems on other phones.
Following speedplane's suggestions I came up with the following code. If the MediaPlayer.getVideoHeight() returns 0 in onPrepared then I delay for 1 second and try again. Now if it does not play the first time it usually will play 1 second later. I have seen it take several tries though.
private void videoPlayer(String path){
if (mMediaController == null)
{
mMediaController = new MediaController(this);
mVideoView.setMediaController(mMediaController);
}
if (!mMediaController.isShowing())
mMediaController.show();
getWindow().setFormat(PixelFormat.TRANSLUCENT);
mVideoView.setVideoPath(path);
mVideoView.requestFocus();
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mp) {
mVideoView.seekTo(mImageSeek);
if (mp.getVideoHeight() == 0) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mVideoView.stopPlayback();
mMediaController = null;
videoPlayer(mImageFilepath);
}
}, 1000);
} else
mVideoView.start();
}
});
}
I have been studying this bug 2 weeks , and i have understand more or less the real problem . Of course i'm talking about The Evil Info 35 Message . If you have met this wonderfull strange error , now you can be happy and shot some vendors like samsung :D ...
The problem comes cause the surface view can't be seen by the user (activity in tabhost or pause-wakeup function , not in focus sometimes , not in front sometimes too (maybe)).
That's cause when some reset that video it runs ok , cause it gains focus or maybe another activity/object is not overlapping it.
When some of those happen , the mediaplayer thinks you can't see the video or it can't play , and it disable video track, giving you the worst headache in your worklife while listening the music track.
Will see next bug , good luck . :D
I had that problem too on my Desire HD about 1 or 2 times. I tried many things but the error was allways there. Afterall i choosed to install a custom rom on my device. Afterwards it worked perfectly and I never had a this issue again.
I know its not that kind of answer you'd like to hear but i didn't find an other soultions.
Here you find custom roms for your device: XDA Developers
I hope I could help you out.
Best Regards and Happy Movie watching
safari =)
The fundamental question is:
before surfaceCreated invoked you start video playback, when the holder is not ready for MediaPlayer, so that you only hear the sound but can not see the picture!
The right way is:

Categories

Resources