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:
Related
I'm new to the audio world so sorry if I'm not using the right terminology.
I'm wondering if it's possible to dynamically play ranges of an mp3 file. So when I need it to I could specify a range and have only that range play from the mp3. I thought that the clipping function Exo Player has might work but I don't want to have to release the source and recreate it every time I want to move the range in the file.
In my research I kept coming across this enhancement request: https://github.com/google/ExoPlayer/issues/3163
I've looked over the release notes for Exo Player but haven't seen anything like that since the mentioned release. Is what I'm trying achieve possible? If so how would I implement something like that? It seems like I'm missing something really simple.
Thanks!
After a bit more research I came across this: https://github.com/google/ExoPlayer/issues/2828
The suggested pattern is to use a Handler and Runnable to achieve what I'm after.
After some fiddling here is what I got working:
import android.os.Handler
fun play() {
if (!player.isPlayer) {
player.seekTo(0)
player.playWhenReady = true
Handler().postDelayed(Runnable {
pause()
}, 1000)
}
}
I initially ran into some strange problems with the handler because it had imported a different library.
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.
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.
I'm making an app where a BroadcastReceiver intercepts an outgoing call and starts an activity with an AlertDialog right before the phonecall is actually placed. I've tested on several phones with different results. On two of the phones I've tested everything works great, the activity is started before the call is placed and therefore "interupts" the phonecall. After the activity is shut down, the phonecall resumes as normal.
On the third phone, an HTC, this doesn't work. The phonecall is placed before the activity starts. How can I prevent this? Are there any priorities I should be looking for?
Welcome to the wide world of HTC headaches when it comes to android development, specifically regarding broadcast receivers. Sense always seems to handle things differently, or in some special way. Though this isn't really the best answer, it is too long for a comment. After looking around a bit, this is a known issue with HTC. A quick fix seemed to have been found at Problem with interecepting outgoing calls on HTC Desire, though somewhat outdated.
They state that you can try
As it seems the HTC Desire (2.2) responds to setResultData(null)
which will stop the out dial. Then you can place a new intent
(Action.CALL) to call the new number. Not so nice workaround, but as
a user you hardly notice it.
Again I am not even sure if this works, or is the answer you are looking for, but it was too much for a comment :P
Before you spend more time reading this than necessary: I will be answering this question myself. I spent a full day debugging this and thought I should share.
I am the developer for an application that is basically a sort of turn-by-turn navigation on golf courses. As such, I have images that need to be rotated to make the image north-oriented. This obviously requires some matrix transformations.
After Android 4.3 got pushed to the HTC One, I got a few complaints that the app was simply closing whenever someone wanted to use it. "Unfortunately has stopped."
My initial debug session didn't clear anything up. There was no exception being thrown, so nothing to step through initially. I found the following in my logs:
11-12 14:12:56.257: ASSERT/libc(5206): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1), thread 5206 (ndroid.appname)
With nowhere to really start debugging, I was in for a long run of old-school stepping through everything, identifying likely suspects and lots of caffeine but in the end I figured it out with the help of a colleague who had written the code that turned out to be the culprit.
As it turns out, this line was causing all the issues:
canvas.setMatrix(null);
The intent of this line was simple: reset the transformation matrix back to the identity matrix so that a new transformation could be applied. According to the documentation, this is allowed:
Completely replace the current matrix with the specified matrix. If the matrix parameter is null, then the current matrix is reset to identity.
Apparently the people working on Android removed this check for a null parameter from the 4.3 code but didn't deem it necessary to either update the documentation or throw a decent exception. Instead, this line just crashes everything without so much as a clue to where it happened and why.
I have filed a bug report if anyone's interested. I hope that it or this thread help someone in the same situation.