I have lots of problems with MediaPlayer.
It's likely pertaining to old OS versions, at least until 2.3.
One sound is playing fine in a certain Activity (right before launching another)
When I pop the second Activity to go back to the first one, I play the sound file right before the popping (I use super.onBackPressed).
On these old devices (~2.3), the sound file is cut off.
I use the same way of launching and playing the MediaPlayer.
I create the MediaPlayer using create (synched). I retain the MediaPlayer by putting it in a static container. I released end empty the container only on setOnCompletionListener. What's going on..
Here's some code...
// Start sound
adhocplaysoundstatic(R.raw.soundfile, getApplicationContext());
// Pop activity
super.onBackPressed();
Definition
static ArrayList<MediaPlayer> staticplayers = new ArrayList<MediaPlayer>();
static public void adhocplaysoundstatic(final int MEDIA, Context context) {
MediaPlayer player = getMediaPlayerStatic(MEDIA, context);
staticplayers.add(player); // Hold on to
player.setOnCompletionListener(new OnCompletionListener() {
#Override public void onCompletion(MediaPlayer mp) {
mp.release();
staticplayers.remove(mp);
}
});
player.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
Log.d(LOG_TAG, "onPrepared, play!");
mp.start();
}});
//player.start();
}
More...
static protected MediaPlayer getMediaPlayerStatic(int id, Context context) throws RuntimeException {
MediaPlayer player = MediaPlayer.create(context, id);
if (player == null) {
throw new RuntimeException("Failed creating media player from id " + id);
}
else {
player.setOnErrorListener(new OnErrorListener() {
#Override public boolean onError(MediaPlayer mp, int what, int extra) {
Log.d(LOG_TAG, "MediaPlayer error: " + mp.toString() + ", what: " + what + ", extra: " + extra);
return false;
}
});
}
return player;
}
Related
I am working in a gallery of images that include an audio of how the things that the user is viewing on the screen are pronounced, which is executed with a button also included in the screen.
The following code works, but sometimes the audio is cut before it finishes reproducing, the problem with this is that I am not receiving any error log.
View.OnClickListener eventoPlay = (v) → {
#Override
public void onClick(View v) {
btnPlaySound.setClickable(false);
try{
Uri myUri = Uri.parse(urlroot + urlAudio); //global variables by current image
final MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
btnPlaySound.setClickable(true);
}
});
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
btnPlaySound.setClickable(true);
Log.e("Error in media player", String.valueOf(what));
Log.e("Error in media player", String.valueOf(extra));
return false;
}
});
mediaPlayer.prepareAsync();
}catch (Exception e){
e.printStackTrace();
btnPlaySound.setClickable(true);
}
}
};
btnPlaySound.setOnClickListener(eventoPlay);
btnPlaySound.performClick();
These are the scenarios that I have debugged:
-setOnPreparedListener() call naturally, always (the audio starts playing always).
-setOnCompletionListener() call only if the audio was finished playing (if the audio was not cut).
-setOnErrorListener() It never call, regardless of whether the audio is cut.
-catch This block is never executed either, regardless of whether the audio is cut.
I tested in devices like Samsung tab S3, Samsgung tab E, Samsung tab A, Alcatel A3, Xiaomi MiA2, Swissmobility.
In the most powerful devices happens less frequently. but I want this failure never to occur.
Tnks.
You may want to ensure btnPlaySound.setClickable(true) is being called on the UI thread. It could be causing an error. Also the media player may be getting garbage collected before finishing keep a (strong) reference to the MediaPlayer to prevent garbage collection.
Try this to test if it being garbage collected:
View.OnClickListener eventoPlay = (v) → {
#Override
public void onClick(View v) {
btnPlaySound.setClickable(false);
try{
Uri myUri = Uri.parse(urlroot + urlAudio); //global variables by current image
// KEEP A STRONG REFERENCE
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.setLooping(false);
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
btnPlaySound.setClickable(true);
}
});
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.release();
btnPlaySound.setClickable(true);
Log.e("Error in media player", String.valueOf(what));
Log.e("Error in media player", String.valueOf(extra));
return false;
}
});
mediaPlayer.prepareAsync();
}catch (Exception e){
e.printStackTrace();
btnPlaySound.setClickable(true);
}
}
};
btnPlaySound.setOnClickListener(eventoPlay);
btnPlaySound.performClick();
What I'm attempting:
I'm using youtube-dl in a Python Daemon on a remote server to get a URL.
That URL is fed into an Android App into a MediaPlayer instance.
What is happening:
Occasionally and unexpectedly the Media player will BLAST static and play at normal speed, sometimes it will blast static and play at 1.5 times speed.
Here's a video of what happens. HEADPHONE WARNING
YouTube Video
Observations:
If there is static it is for the whole song (it isn't intermittent).
I've taken the URLs it provides and they play fine in a PC browser with no static.
It happens on different phones, and it is not just my particular phone.
It takes longer to start tracks that end up being staticy.
Tracks that are staticy make my progress bar (seconds minutes display) just behave strangely. I've seen it count up and down in the first couple seconds, and there is the 1.5x speed I was talking about.
MediaHTTPConnection throws alot of exceptions that I don't know how to handle.
E/MediaHTTPConnectionEx: disconnecting
E/MediaHTTPConnectionEx: RuntimeException: Unbalanced enter/exit
mConnection.disconnect();
Below is the portion of my Python daemon that returns the URL
ydl_opts = {
'skip_download':True, # We just want to extract the info
'format':'bestaudio',
'forceurl':True,
'ignoreerrors':True,
'youtube_include_dash_manifest':False,
'restrict_filenames':True,
'source_address':'10.1.0.38',#we have to set this to force ipv4
'logger': MyLogger()
}
def ytdl(self, url):
url2 = "https://www.youtube.com/watch?v="+url
ydl.download([url2])
Here's the (basically boilerplate) MediaPlayer
public static Stack<Track> tracks = new Stack<>();
private static MediaPlayer mediaPlayer;
private String mediaFile;
private static int duration = 0;
private AudioManager audioManager;
private Boolean userPause = false;
// Binder given to clients
private final IBinder iBinder = new LocalBinder();
public static final String TAG = "Player";
#Override
public IBinder onBind(Intent intent) {
return iBinder;
}
class LocalBinder extends Binder {
Player getService() {
return Player.this;
}
}
public static void seekTo(int msec){
if(mediaPlayer != null){
mediaPlayer.seekTo(msec);
}
}
//The system calls this method when an activity, requests the service be started
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
boolean success = true;
//An audio file is passed to the service through putExtra();
if(intent.hasExtra("uri")){
mediaFile = intent.getStringExtra("uri");
} else {
stopSelf();
success = false;
}
//Request audio focus
if (!requestAudioFocus()) {
//Could not gain focus
Log.d(TAG, "error requesting audio focus");
stopSelf();
success = false;
}
if (mediaFile != null && !mediaFile.equals("") && success) {
Log.d(TAG, "Media File:" + mediaFile);
success = initMediaPlayer();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
//we cant destroy the player here because the back button fires this
//maybe i can not fire super?
super.onDestroy();
/*if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.release();
}
removeAudioFocus();*/
}
private boolean initMediaPlayer() {
boolean error = false;
//one time setup
if(mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
//setup listeners
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
//Reset so that the MediaPlayer is not pointing to another data source
mediaPlayer.reset();
try {
Log.d(TAG, "setDataSource");
mediaPlayer.setDataSource(mediaFile);
} catch (IOException e) {
Log.e(TAG,"setDataSource error:"+e);
error = true;
}
try {
Log.d(TAG, "prepare");
mediaPlayer.prepare();
} catch (IOException e) {
Log.d(TAG, "prepare error");
e.printStackTrace();
error = true;
}
return error;
}
#Override
public void onPrepared(MediaPlayer mp) {
//Invoked when the media source is ready for playback.
Log.d(TAG, "onPrepared");
mp.start();
duration = mp.getDuration();
}
#Override
public void onCompletion(MediaPlayer mp) {
//Invoked when playback of a media source has completed.
removeAudioFocus();
mp.stop();
mp.reset();
}
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
//Invoked to communicate some info.
return false;
}
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
//Invoked indicating buffering status of
//a media resource being streamed over the network.
if(percent%25==0)
Log.d(TAG, "buffering:"+percent);
}
#Override
public void onSeekComplete(MediaPlayer mp) {
//Invoked indicating the completion of a seek operation.
Log.d(TAG, "onSeekComplete() current pos : " + mp.getCurrentPosition());
SystemClock.sleep(200);
start();
}
//Handle errors
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
//Invoked when there has been an error during an asynchronous operation
switch (what) {
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
Log.e(TAG, "MEDIA ERROR NOT VALID FOR PROGRESSIVE PLAYBACK " + extra);
break;
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
Log.e(TAG, "MEDIA ERROR SERVER DIED " + extra);
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
Log.e(TAG, "MEDIA ERROR UNKNOWN " + extra);
//NowPlaying.error = true;
break;
default:
Log.e(TAG, what + "," + extra);
break;
}
PlayerActivity.error = true;
return false;
}
#Override
public void onAudioFocusChange(int focusState) {
//Invoked when the audio focus of the system is updated.
switch (focusState) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
mediaPlayer.setVolume(1.0f, 1.0f);
if(!mediaPlayer.isPlaying()
&& !userPause) {
pause(false);
}
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
removeAudioFocus();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
// is likely to resume
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (mediaPlayer.isPlaying()) mediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}
private boolean requestAudioFocus() {
int result = 0;
if(audioManager == null) audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (audioManager != null) {
result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
Log.d(TAG, "requestAudioFocus:"+result);
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
//Could not gain focus
}
private void removeAudioFocus() {
audioManager.abandonAudioFocus(this);
}
boolean isPlaying() {
if(mediaPlayer != null)
return mediaPlayer.isPlaying();
return false;
}
//pause(true) == pause
//pause(false) == play
//this is used by the system
void pause(Boolean state){
//pause
if (state) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
} else {
if (!mediaPlayer.isPlaying()) {
start();
}
}
}
//this is a pause toggle that is only triggered by the pause/play button
boolean pause() {
if (mediaPlayer.isPlaying()){
userPause = true;
mediaPlayer.pause();
} else {
userPause = false;
start();
}
return !mediaPlayer.isPlaying();
}
void start(){
mediaPlayer.start();
}
int getCurrentPosition(){
if(mediaPlayer != null)
return mediaPlayer.getCurrentPosition();
return 0;
}
int getDuration(){
return duration;
}
}
I feel like someone else is going to have this problem so I'm going to post my solution.
So I noticed a mime type error popped up for every track. The error still shows up now that I've fixed this problem but the loud static has stopped.
Here is the error that started the wheels turning:
E/MediaHTTPConnectionEx: getMIMEType
[seekToEx] offset:0/mCurrentOffset:-1
I noticed some of the URLs youtube-dl was giving me for the webm didn't have a mime type specified in the URL.
Here is an example:
...O8c2&mime=&pl=...
But all of the m4a streams had a mime type in the URL
...70F61&mime=audio%2Fmp4&itag=140&key...
So I think that while my solution isn't the best solution, it's the easiest. Since ALL the m4a streams had a mime specified I just limited myself to those streams.
The rub is this:
I'm pretty sure that if I just checked the URL for a specified mime field I could still play most webm files. The only ones that were failing (staticy) were URLs that did not have that field.
My solution:
Python only pulls m4a files:
...
'format':'bestaudio[ext=m4a]',
...
Android now passes hard coded headers:
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "audio/mp4"); // change content type if necessary
Uri uri = Uri.parse(mediaFile);
Log.d(TAG, "getMimeType="+getMimeType(uri));//this is ALWAYS null
mediaPlayer.setDataSource(getApplicationContext(), uri, headers);
I have a piano app working with this method:
public void play(String note) {
score++;
score();
try {
mp = MediaPlayer.create(this, getResources().getIdentifier(note, "raw", getPackageName()));
mp.start();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer media) {
media.release();
}
});
} catch (Exception e) {
Log.e("Error", "error playing file : " + note + "\n" + e);
}
}
The issue is that if I press the keys too fast and several times i get errors like this:
E/MediaPlayer: error (1, -19)
These errors occur as I go on playing. But when I uninstall the app, something seems to reset, and I get less errors...Why is this happening and is there a solution for it?
As your code implies you are playing notes and not long MP3s, I suggest you use SoundPool instead of MediaPlayer. It is better suited for this kind of application as it pre-loads all the resources in advance.
An example of this kind of implementation:
private SoundPool soundPool;
private HashMap<String, Integer> soundNameToSoundIdMap;
private void initSoundMap() {
soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
soundNameToSoundIdMap = new HashMap<String, Integer>();
soundNameToSoundIdMap.put("note_a", loadSound(getContext(), R.raw.note_a));
soundNameToSoundIdMap.put("note_b", loadSound(getContext(), R.raw.note_b));
}
private int loadSound(Context context, int resId) {
return soundPool.load(context, resId, 1);
}
public void play(String note) {
score++;
score();
Integer soundId = soundNameToSoundIdMap.get(note);
if (soundId != null){
soundPool.play(soundId.intValue(), 100, 100, 1, 0, 0);
}
}
I am new to Android Auto and still trying to figure it out.
I have successfully played music from my own programmed app but the music is coming out from my smartphone speakers instead of car speakers. Other (sample) apps do it the right way.
Which part of of the Media System is responsible to handle this behaviour? Android documentation says the sound is sent to the car speakers.
#Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
this.mPlayingQueue.add(item);
session.setActive(true);
session.setQueue(mPlayingQueue);
session.setQueueTitle("My Queue");
session.setPlaybackState(buildState(PlaybackState.ACTION_PLAY));
session.setMetadata(createRammsteinMetaData());
this.mediaPlayer = MediaPlayer.create(getBaseContext(), R.raw.rammstein_sonne);
this.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
if (tryToGetAudioFocus()) {
this.mediaPlayer.start();
Log.d("AUDIOTAG", "Playing");
} else {
Log.d("AUDIOTAG", "Playing not possible, no focus");
}
}
private boolean tryToGetAudioFocus() {
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
Thanks in advance. Orrimp
There seems to be a pretty big bug there! Local music from resources does not play properly using MediaPlayer.create(...);
Thx Reaz Murshed I just tried to use the STREAM_MUSIC as a real stream with Internet Music and it works.
Translate to use setDataSource with Resource URI!
Use the following snippet:
#Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
/* Set session stuff like queue, metadata and so on*/
Uri myUri = resourceToUri(getBaseContext(), R.raw.rammstein_sonne);
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getBaseContext(), myUri);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if (tryToGetAudioFocus()) {
mediaPlayer.start();
Log.d("AUDIOTAG", "Playing");
} else {
Log.d("AUDIOTAG", "Playing not possible, no focus");
}
}
});
} catch (IOException e) {
e.printStackTrace();
Log.d("AUTO", "EERORROR");
}
}
public Uri resourceToUri(Context context, int resID) {
return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
context.getResources().getResourcePackageName(resID) + '/' +
context.getResources().getResourceTypeName(resID) + '/' +
context.getResources().getResourceEntryName(resID));
}
I have a application in which when i select any item then it will play that media file but when i select other item then the old media is playing continue and the current media file is overlay(current file is also playing) so i am listen both media files.
I have written below code:
ImageView songView;
Gallery songGallery;
MediaPlayer mp;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song_test);
songView = (ImageView) findViewById(R.id.songView);
songGallery = (Gallery) findViewById(R.id.songGallery);
songGallery.setAdapter(new MyGalleryAdapter(getApplicationContext()));
songGallery.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View arg1,
int arg2, long id) {
// TODO Auto-generated method stub
songView.setImageResource(symbolIds[arg2]);
String imageName = getResources().getResourceEntryName(
symbolIds[arg2]);
Uri audio = Uri.parse("android.resource://" + getPackageName()
+ "/raw/" + imageName + "");
mp = MediaPlayer.create(getApplicationContext(), audio);
try {
if (mp.isPlaying()) {
mp.reset();
}
mp.start();
} catch (Exception e) {
Toast.makeText(getApplicationContext(),
"Error", Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), e.toString(),
Toast.LENGTH_LONG).show();
}
....
....
}
}
So my question is - When i select the item the song is playing very well but when i select other item then the old song is also playing with the current.
so i want to stop the old song...
I want to play only current selected item song.
The issue is that MediaPlayer is being recreated instead of being reset. Rather than using the #create() factory call, you can use the constructor instead. In the following code, if the mp is already created it is reset and then a datasource is prepared and started.
if (mp != null) {
mp.reset();
} else {
mp = new MediaPlayer();
}
// Now set the datasource
mp.setDataSource(context, audio);
mp...
mp.prepare();
mp.start();
Ideally though you should call prepareAsync and have a callback onPrepare() call the actual mediaPlayer.start so that prepare does not block the main thread.
mp.setOnPreparedListener(this);
mp.setDataSource(..)
mp.prepareAsync();
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
Make the MediaPlayer mp global variable private static. Better yet use Singleton instead of global
You could review the state diagram here
then review an implementation of that logic for recycle media player in the
media sample you can find in your SDK installation:
$SDK/.../src/com/android/sample/apis/media/MediaPlayerDemo_Video.java
and follow how the sample resets the player state before using it again