MediaPlayer: buffer status for internet radio stream? - android

I am playing internet radio streams using MediaPlayer. However buffering seems to take about 10 seconds for me each time I start playing a stream. Is there any way I can show:
The progress when starting to play? E.g. a progress bar in the GUI
The buffer status once playing? (e.g. to give a warning when buffer is running dry for some reason)
I tried using setOnBufferingUpdateListener() but as far as I can tell, it only works for streams where the duration is known. (So not applicable to internet radio, unless I'm wrong - it was not being fired)

Implements MediaPlayer.OnInfoListener, overriding the method onInfo you have action to control the status of the MediaPlayer as follow:
#Override
public boolean onInfo(MediaPlayer arg0, int what, int extra) {
Log.i(LOG_TAG, "MediaPlayer INFO code = " + what + " extra=" + extra);
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
Log.d(LOG_TAG, "Start buffering...");
} else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
Log.d(LOG_TAG, "End buffering...");
Log.d(LOG_TAG, "Start playing...");
}
return false;
}
Take a look at http://developer.android.com/reference/android/media/MediaPlayer.html for other actions;)

Related

MediaPlayer plays audio but not video in programmatically created `Surface`

I am working with some code that creates a Surface programmatically and uses it to display the camera preview. I am trying to modify it to instead display a video. The changed code (below) plays the video's audio but there is no video--just a black screen.
The camera preview version works fine, so I don't think the issue is with how the SurfaceTexture or Surface are created or displayed.
int texid = getTexture(); //native method
mSurfaceTexture = new SurfaceTexture(texid);
Log.e(TAG, "texid is "+texid);
mSurfaceTexture.setOnFrameAvailableListener(new OnFrameAvailableListener() {
#Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
Log.d(TAG, "onFrameAvailable");
}
});
final Surface surface = new Surface(mSurfaceTexture);
mediaPlayer.setSurface(surface);
String mSourceString = "clips/key_frames_movie_small.mp4";
AssetManager assetManager = mContext.getResources().getAssets();
AssetFileDescriptor clipFd = assetManager.openFd(mSourceString);
mediaPlayer.setDataSource(clipFd.getFileDescriptor(),
clipFd.getStartOffset(),
clipFd.getLength());
clipFd.close();
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(TAG, "ERROR: "+what + ", " + extra);
return false;
}
});
mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.e(TAG, "INFO: "+what + ", " + extra);
return false;
}
});
mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
#Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
Log.e(TAG, "onVideoSizeChanged: "+width + ", " + height);
}
});
mediaPlayer.prepare();
mediaPlayer.start();
Things I've tried:
Using the MediaPlayer to play the same video in a SurfaceView that's in the xml layout. This works fine, so I don't think there's any problem with the video itself.
Removing background colors from xml, as suggested here. No effect.
Playing a small video (320x240) (screen dimens are 1920x1080) in case resolution was a problem, as described here
Used logs to verify that the surface was created before calling mediaPlayer.setSurface(surface) as suggested here.
Called surfaceView.setZOrderOnTop(true), surfaceView.setZOrderMediaOverlay(true), surfaceView.setVisibility(View.VISIBLE) to make sure the view isn't partially hidden by anything. No effect.
Called holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) as described here out of desperation, even though I'm running on 6.0.1.
How can I get the video to play in the surface without changing how the surface texture or surface are created?
Edit:
Logs look like this:
E/com.package: texid is 1
D/MediaPlayer: setSubtitleAnchor in MediaPlayer
W/MediaPlayer: info/warning (3, 0)
D/MediaPlayer: setSubtitleAnchor in MediaPlayer
E/com.package: onVideoSizeChanged: 960, 540
E/com.package: onVideoSizeChanged: 960, 540
E/com.package: INFO: 3, 0
D/com.package: onFrameAvailable
According to the docs, the info code 3 indicates that "The player just pushed the very first video frame for rendering."
onFrameAvailable is called only once. If I do surfaceTexture.updateTexImage(); inside the onFrameAvailable() callback, there are (perhaps once a second) additional calls to onFrameAvailable(), but it doesn't affect the display, which stays black.

Android stop sounds

I am writing an alert app. When an alert is received, I want to stop all current sounds (like music) and play my alert sound. After the user has done something and the activity will be closed, the previous sounds should continue.
Thats the same like someone calls you. How can I achieve this behaviour?
You can request audio focus. Once you are done with your own audio, abandon audio focus so other apps can start playing sound again. You will be notified of other apps requesting audio focus too, thus being able to pause and resume your own sounds.
Requesting audio focus:
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
// Start playback.
}
With the help of Julius Skripkauskas' answer I use the following code:
to start the alert sound:
private void playSound(){
try{
Log.d(TAG, "request audio focus");
this.am=(AudioManager)getSystemService(Context.AUDIO_SERVICE);
int result=this.am.requestAudioFocus(afListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE);
if(result==AudioManager.AUDIOFOCUS_REQUEST_GRANTED){
Log.d(TAG, "audio focus granted");
this.player=new MediaPlayer();
this.player.setOnPreparedListener(new OnPreparedListener(){
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}});
this.player.setOnCompletionListener(new OnCompletionListener(){
#Override
public void onCompletion(MediaPlayer mp) {
am.abandonAudioFocus(afListener);
}});
this.player.setAudioStreamType(AudioManager.STREAM_MUSIC);
this.player.setDataSource(getApplicationContext(), Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.sound ));
this.player.prepareAsync();
}
}catch(Exception e){
Log.e(TAG, "error playing music." + e.getMessage(), e);
}
}
The Listener:
I simply stop the sound because I will only play the sound once focus was granted. If the sound will get interrupted by an other app, it doesn't matter...
private AudioManager.OnAudioFocusChangeListener afListener = new AudioManager.OnAudioFocusChangeListener(){
#Override
public void onAudioFocusChange(int focusChange) {
Log.d(TAG, "audio focus changed" + Integer.toString(focusChange));
if(player==null){
// we don't play audio
return;
}
player.stop();
}};
It is important to consider to add the OnCompletionListener to the player where to call am.abandonAudioFocus(afListener) to release the audio focus immediately after the sound was played.

Video stream lag and disconnect with videoview using ip camera stream

So I have an IP camera that outputs a video stream I can connect to via the rtsp protocol. I want to display this in my android application, so I've set up a videoview using the following code;
VideoView vv = (VideoView)this.findViewById(R.id.videoView);
Uri uri = Uri.parse("rtsp://username:password#192.168.0.1:554/1/stream3");
vv.setVideoURI(uri);
vv.requestFocus();
vv.start();
I'm putting this in the onCreate() of the main activity class, so when the app loads up it automatically connects and starts streaming. My experience with this is that it works - but eventually gets choppy and or just stops randomnly and doesn't seem to ever get back to running again. I have to close the app and clear it from memory and restart it to get it back - but then it loses connection shortly after, meaning its pretty much useless.
I also found it seemed to lag a bit when touching on the screen objects like menus or buttons but that might just be a coincidence - I can't say for sure.
The thing is the stream is perfect from a PC on the same network via VLC using the same URL. So what am I doing wrong, and is there any better method of handling streaming video? I ultimately wanted to mate the videoview with some overlaid text and buttons, and potentially take screenshots when necessary. At the moment I'm lucky if I get video for a few seconds before it cuts out...
Some additional comments;
I've had some success running it for a longer frame of time - so it's not always bad which makes things difficult to diagnose. But when it stops it stops.
Does videoview actively try to reconnect if it has lost a connection?
Is there a way of demonstrating this with a progress indicator perhaps - so it doesn't look like it's doing nothing?
A bit late, but for others with the same problem: try debugging by setting listeners to your VideoView? i.e. onError, onCompletion, etc.
For example:
vv.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.d("VideoViewError", Integer.toString(what));
//logs the error you're running into
//You can also put a switch case here to
determine what error it is running into exactly:
String errorString = "Media Player Error: ";
switch (what) {
case MediaPlayer.MEDIA_ERROR_UNKNOWN: {
errorString += "Unspecified media player error. ";
}
case MediaPlayer.MEDIA_ERROR_SERVER_DIED: {
errorString += "Media server died. ";
}
}
switch (extra) {
case MediaPlayer.MEDIA_ERROR_IO: {
errorString += "File or network related operation error.";
}
case MediaPlayer.MEDIA_ERROR_MALFORMED: {
errorString += "Bitstream is not conforming to the related coding standard or file spec.";
}
case MediaPlayer.MEDIA_ERROR_UNSUPPORTED: {
errorString += "Bitstream is conforming to the related coding standard or file spec, but the media framework does not support the feature.";
}
case MediaPlayer.MEDIA_ERROR_TIMED_OUT: {
errorString += "Media operation timed out.";
}
}
Log.d(TAG, errorString);
return true;
}
});
If the stream is 'ending', you will get an onCompletion
setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.d("VideoViewError", "Media Player reached end of file");
}
}
);
You can do likewise with setOnInfoListener, which lets you know the status of the video view during playback. (Codes are here: http://developer.android.com/reference/android/media/MediaPlayer.OnInfoListener.html)
Maybe not the answer you're looking for, but will hopefully lead you to the right one!

Android: VideoView setOnErrorListener called only once

Below is the piece of my code for handling the error of my video player. This error callback listener gets triggered for the first time only. After that, it's not capturing the error.
videoPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.msgPleaseNoConnection),
Toast.LENGTH_SHORT).show();
vVideoBufferLoader.setVisibility(View.INVISIBLE);
return false;
}
});
Note:
I tried returning true from that callback which means I handled the error. But it doesn't solve the problem too.
The goal of the MediaPlayer's OnErrorListener is to signal when an error has occurred, at which point the MediaPlayer object is in an end state.
http://developer.android.com/reference/android/media/MediaPlayer.html
If you are using the MediaPlayer constructor to 'reset' the object elsewhere in the code, you are essentially creating a new MediaPlayer object and saving it over the older one. If this is the case, then you also need to reassign the OnErrorListener.
Here's a short snippet of how I've been using OnErrorListener in my app:
private MediaPlayer.OnErrorListener vidVwErrorListener = new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) { //if there was an error in trying to play the intro video
if (tryLrgClip) { // If the larger-resolution clip failed to play, try playing the backup (lower-resolution) clip.
tryLrgClip = false;
trySmClip = true;
vidVwSplashView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + SPLASH_VIDEOS));
vidVwSplashView.start();
} else { // If that didn't work either, give up on playing a video, and do something else
tryLrgClip = trySmClip = false;
vidVwSplashView.setVisibility(View.GONE);
//Something else
}
return true;
}
};
I hope that helps!

Android VideoView MediaPlayer OnInfoListener - events not fired

this following source code snippet is given:
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END){
activity.dismissDialog(DialogID.DIALOG_LOADING);
return true;
}
return false;
}
});
}
});
I am streaming HLS streams with Android 3.x+ devices and trying to hide a loading dialog once the buffering is completed.
The video streaming works, but the info events are never fired.
Any ideas?
I know its too late, But posting it for the users still seeking for the solution (This worked for me):
progressDialog.show();
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END){
progressDialog.dismiss();
return true;
} else if(what == MediaPlayer.MEDIA_INFO_BUFFERING_START){
progressDialog.show();
}
return false;
}
});
progressDialog.dismiss();
videoView.start();
}
});
You're right, the events are never fired. This is a known HLS bug that I don't think Google will fix.
This applies to the onInfo and the buffering events.
See https://code.google.com/p/android/issues/detail?id=42767 and https://code.google.com/p/googletv-issues/issues/detail?id=2
Sorry!
Not fully sure as to what the OP is asking, but here are some very untimely bits of information.
I wouldn't rely on onPrepared. I find it to be unreliable.
I have found the two most useful pieces of information for HLS streaming through the MediaPlayer are the duration of the video and the progress position of the video. You get both of these by listening to progress updates.
When the duration is greater than zero, you know the video is truly prepared and can be manipulate (scrub). When progress position changes, you know the video is done buffering and has commenced playback. This last item only works when the video is playing of course. The MediaPlayer tends to relay inaccurate information.
These pieces of information are mostly accurate and can usually be relied upon to be "fairly" timely. This timeliness varies from device to device.
onPrepared is called when the MediaPlayer is prepared to start buffering, not when the video is completely buffered. However, it is completely natural to dismiss the loading dialog from within the onPrepared method.
Also MEDIA_INFO_BUFFERING_END is used when MediaPlayer is resuming playback after filling buffers, so I do not think it should be something to use to dismiss the dialog. So this should work:
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
activity.dismissDialog(DialogID.DIALOG_LOADING);
}
});
You can able to set OnPreparedListener on videoView because its your object but if you checkout source of VideoView you will find that mMediaPlayer is its private member so any change that you do from external will not be applied to it.
As per your requirement you need buffering status so you can have thread or handler or some thing so you can update your UI to get buffer status there is one method
int percent = videoView.getBufferPercentage();
if(percent == 100){
// buffering done
}
You no need to go through setOnInfoListener
by overriding setOnPreparedListener method is enough. as in the api show
public void setOnPreparedListener (MediaPlayer.OnPreparedListener l)
Register a callback to be invoked when the media file is loaded and
ready to go.
so, you can dismiss your dialog inside setOnPreparedListener method is enough
like this
vv.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "finish11", Toast.LENGTH_LONG).show();
}
});
}
});
If you want to show loading each time it's buffering (initial time or subsequent buffer underruns) just ensure to show it again:
// at the beginning
show
boolean onInfo(int what, int extra) {
switch (what) {
case MEDIA_INFO_BUFFERING_END:
"hide";
break;
case MEDIA_INFO_BUFFERING_START
"show":
}
}
So this event sequence will do as desired:
- whenever you start (setVideoURI or start): show
- onPrepared: just plug the info listener
- onInfo BUFFERING_END hide (it's playing)
- onInfo BUFFERING_START show (it's buffering again)
- onInfo BUFFERING_END hide (it's playing)
Update:
This is assuming the info events work. Of course.

Categories

Resources