I'm working on Video streaming over Delay-Tolerant Networks using android mobiles. For that im trying to implement progressive video download based on the tutorial given by pocket journey
http://blog.infidian.com/2008/04/04/tutorial-custom-media-streaming-for-androids-mediaplayer/
While creating a new media player the prepareAsync function is blocking the video display, making streaming unbearable. Is there a way to display something while the media player is prepared?
I'm using SurfaceView and SUrfaceHolder to display the video as suggested in the API media demos.
private MediaPlayer createMediaPlayer(File mediaFile)
throws IOException {
MediaPlayer mPlayer = new MediaPlayer();
mPlayer.setOnErrorListener(
new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(TAG, "Error in MediaPlayer: (" + what +") with extra (" +extra +")" );
return true;
}
});
// It appears that for security/permission reasons, it is better to pass a FileDescriptor rather than a direct path to the File.
// Also I have seen errors such as "PVMFErrNotSupported" and "Prepare failed.: status=0x1" if a file path String is passed to
// setDataSource(). So unless otherwise noted, we use a FileDescriptor here.
FileInputStream fis = new FileInputStream(mediaFile);
mPlayer.setDataSource(fis.getFD());
mPlayer.setDisplay(holder);
mPlayer.prepareAsync();
return mPlayer;
}
/**
* Transfer buffered data to the MediaPlayer.
* NOTE: Interacting with a MediaPlayer on a non-main UI thread can cause thread-lock and crashes so
* this method should always be called using a Handler.
*/
private void transferBufferToMediaPlayer() {
try {
// First determine if we need to restart the player after transferring data...e.g. perhaps the user pressed pause
final boolean wasPlaying = mediaPlayer.isPlaying();
final int curPosition = mediaPlayer.getCurrentPosition();
Log.e(TAG, "Current position: "+curPosition);
final MediaPlayer temp = mediaPlayer;
// Copy the currently downloaded content to a new buffered File. Store the old File for deleting later.
File oldBufferedFile = new File(context.getCacheDir(),"playingMedia" + counter + ".dat");
File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".dat");
// This may be the last buffered File so ask that it be delete on exit. If it's already deleted, then this won't mean anything. If you want to
// keep and track fully downloaded files for later use, write caching code and please send me a copy.
bufferedFile.deleteOnExit();
moveFile(downloadingMediaFile,bufferedFile);
// Pause the current player now as we are about to create and start a new one. So far (Android v1.5),
// this always happens so quickly that the user never realized we've stopped the player and started a new one
if(wasPlaying){
Log.e(TAG, "mediaPlayer paused");
mediaPlayer.pause();
}
//mediaPlayer.release();
// Create a new MediaPlayer rather than try to re-prepare the prior one.
mediaPlayer = createMediaPlayer(bufferedFile);
mediaPlayer.setOnPreparedListener(
new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mPlayer) {
// TODO Auto-generated method stub
mediaPlayer.seekTo(curPosition);
// Restart if at end of prior buffered content or mediaPlayer was previously playing.
// NOTE: We test for < 1second of data because the media player can stop when there is still
// a few milliseconds of data left to play
int mediaLength = mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition();
boolean atEndOfFile = mediaLength >= 1000;
Log.e(TAG, "MediaLen:"+mediaLength);
if (wasPlaying || atEndOfFile){
Log.e(TAG, "starting new player");
temp.release();
mediaPlayer.start();
}
}
});
// Lastly delete the previously playing buffered File as it's no longer needed.
oldBufferedFile.delete();
}catch (Exception e) {
Log.e(TAG, "Error updating to newly loaded content.", e);
}
}
Consider using exoplayer
http://developer.android.com/guide/topics/media/exoplayer.html
it was built with streaming in mind from the docs:
ExoPlayer supports features not currently provided by MediaPlayer, including Dynamic adaptive streaming over HTTP (DASH), SmoothStreaming, and persistent caching. ExoPlayer can be extended to handle additional media formats, and because you include it as part of your app code, you can update it along with your app.
Related
I am using following code to play sound in my app. Everything worked fine before ICS. But on ICS and more recent versions no sound can be heard although there is no error appearing.
EDIT: Note, the following code is triggered by a broadcase receiver. BroadCast receiver invokes a async task. In the post process method of asycn task the following method is called.
This error happens only on specific mobile models (Eg nexus ) and on some models users are able to play sounds which comes with stock os but not their own sounds which they places in rigtones folder.
I am not able get whats the issues as it happens on specific handset
What could the error possibly be?
public static void playSound(final Context context, final int volume,
Uri uri, final int stream, int maxTime, int tickTime) {
//stopPlaying();
/*
if (stream < 0 || stream > 100) {
throw new IllegalArgumentException(
"volume must be between 0 and 100 .Current volume "
+ volume);
}*/
final AudioManager mAudioManager = (AudioManager) context
.getSystemService(Context.AUDIO_SERVICE);
int deviceLocalVolume = getDeviceVolume(volume,
mAudioManager.getStreamMaxVolume(stream));
Log.d(TAG,
"device max volume = "
+ mAudioManager.getStreamMaxVolume(stream)
+ " for streamType " + stream);
Log.d(TAG, "playing sound " + uri.toString()
+ " with device local volume " + deviceLocalVolume);
final int oldVolume = mAudioManager.getStreamVolume(stream);
// set the volume to what we want it to be. In this case it's max volume
// for the alarm stream.
Log.d(Constants.APP_TAG, "setting device local volume to " + deviceLocalVolume);
mAudioManager.setStreamVolume(stream, deviceLocalVolume,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
final MediaPlayer mediaPlayer = new MediaPlayer();
golbalMMediaPlayer = mediaPlayer;
try {
final OnPreparedListener OnPreparedListener = new OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mp) {
Log.d(TAG, "onMediaPlayercompletion listener");
mp.start();
countDownTimer.start();
}
};
mediaPlayer.setDataSource(context.getApplicationContext(), uri);
mediaPlayer.setAudioStreamType(stream);
mediaPlayer.setLooping(false);
mediaPlayer.setOnPreparedListener(OnPreparedListener);
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.d(Constants.APP_TAG, "Entered onCompletion listener of mediaplayer");
mAudioManager.setStreamVolume(stream, oldVolume,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
try{
if(mediaPlayer != null && mediaPlayer.isPlaying()){
mediaPlayer.release();
}
}catch(Exception ex){
Log.e(Constants.APP_TAG, "error on oncompletion listener" ,ex);
}
}
});
CountDownTimer timer = new CountDownTimer(maxTime*1000, tickTime*1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.d(TAG, "tick while playing sound ");
}
#Override
public void onFinish() {
Log.d(TAG, "timer finished");
stopPlaying();
}
};
countDownTimer = timer;
mediaPlayer.prepareAsync();
} catch (Exception e) {
Log.e(TAG, "problem while playing sound", e);
} finally {
}
}
LOGS
:07-01 00:00:00.030: D/beephourly(9500): device max volume = 7 for streamType 5
07-01 00:00:00.030: D/beephourly(9500): playing sound content://media/internal/audio/media/166 with device local volume 7
07-01 00:00:00.030: D/beephourly(9500): setting device local volume to 7
07-01 00:00:00.080: D/beephourly(9500): vibrating with pattern = [J#428bae20
07-01 00:00:00.090: D/beephourly(9500): will show normal notification
07-01 00:00:00.100: D/beephourly(9500): notification is enabled
07-01 00:00:00.100: D/usersettings(9500): hr = 0
07-01 00:00:00.110: D/beephourly(9500): onMediaPlayercompletion listener
07-01 00:00:00.451: D/beephourly(9500): tick while playing sound
07-01 00:00:20.460: D/beephourly(9500): timer finished
07-01 00:00:20.460: D/beephourly(9500): got request to stop playing
07-01 00:00:20.460: D/beephourly(9500): cancelling countdowntimer
07-01 00:00:20.460: D/beephourly(9500): releasing mediaplayer now
This question is a follow up question to question which i asked previously:
sound not playing in android > icecream sandwich
You should be requesting audio focus, even for notifications. In your case it would look something like this:
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//request a transient lock on the notification stream
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_NOTIFICATION,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// could not get audio focus.
}
I suspect there is some other app(s) running on the devices which are giving you problems which are requesting audio focus. If other apps request audio focus, and you do not, it is possible your app will not be played out to the final stream. Requesting focus yourself will ensure no other processes are interfering with your notification sound. Although this was introduced a while ago, newer versions of Android are much more sensitive to apps using this mechanism, especially from the background.
To be proper, when you're done with the notification, you can then release the focus:
audioManager.abandonAudioFocus(this);
I am trying to implement a playlist of videos so they have a smooth transition from one video to the next. The mediaPlayer object has a prepareasync() method that would seem to prebuffer the video so it's ready to play. How do I invoke the prepareasync method from the VideoView object? The examples I found for mediaPlayer either don't use VideoView and seem to create the surface from scratch. Or the examples use mediaPlayer as return parameter on videoview eventlisteners which seem to occur only after the videoview.play(). I would like have access to videoview's mediaplayer before invoking the play method so I can invoke the prepareasync() and then later the the play().
Just as user1023110 mentioned, VideoView is a wrapper around MediaPlayer. Diving into the source code (since the docs aren't useful at all) I confirmed that it internally calls prepareAsync() in its private method openVideo():
private void openVideo() {
if (mUri == null || mSurfaceHolder == null) {
// not ready for playback just yet, will try again later
return;
}
// Tell the music playback service to pause
// TODO: these constants need to be published somewhere in the framework.
Intent i = new Intent("com.android.music.musicservicecommand");
i.putExtra("command", "pause");
mContext.sendBroadcast(i);
// we shouldn't clear the target state, because somebody might have
// called start() previously
release(false);
try {
mMediaPlayer = new MediaPlayer();
if (mAudioSession != 0) {
mMediaPlayer.setAudioSessionId(mAudioSession);
} else {
mAudioSession = mMediaPlayer.getAudioSessionId();
}
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mOnInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
// we don't set the target state here either, but preserve the
// target state that was there before.
mCurrentState = STATE_PREPARING;
attachMediaController();
} catch (IOException ex) {
Log.w(TAG, "Unable to open content: " + mUri, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
return;
} catch (IllegalArgumentException ex) {
Log.w(TAG, "Unable to open content: " + mUri, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
return;
}
}
VideoView is a wrapper around MediaPlayer and SurfaceView so you don't have to do it yourself. I believe the wrapper does the prepare when you call setVideoURI.
Its possible to get hold of the mediaplayer inside the VideoView through the onPrepared eventhandler, and once you've got it, I guess you can call prepare yourself after the first time, but I gather the internals are tricky and not well documented so things might not work as expected.
I use MediaPlayer for playing a single MP3 song from network. Data source is the file downloaded from network. This files comes in chuncks.
Let's assume we have following playing state.
Song duration: 4:00
Current chunck loaded in player: 1:00
Let's say I want to skip some part of a song and seek forward. I do it with MediaPlayer.seekTo() method. If I seek to position (1:40) it is not performed correctly, seek bar goes at start position (00:00) and the playerr stops.
public static void seekTo(int progress) {
try {
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(sFilePath);
mp.prepare();
int offset = (progress * mp.getDuration()) / 100;
if (sCompleted)
return;
sLastSeek = offset;
if (offset > sMediaPlayer.getDuration()) {
sMediaPlayer.reset();
sMediaPlayer.setDataSource(sFilePath);
sMediaPlayer.prepareAsync();
} else {
sMediaPlayer.seekTo(offset);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
I would like to use an arbitrary InputStream as a data source for a MediaPlayer object.
The reason for this is that the InputStream I am using is in fact an authorized HTTPS connection to a media resource on a remote server. Passing the URL in that case will obviously not work as an authentication is required. I can however do the authentication separately and get an InputStream to the resource - problem is what do I do once I have it?
I thought about the option of using a named pipe and passing its FileDescriptor to the setDataResource method of MediaPlayer. Is there a way to create named pipes in Android (without using NDK)?
Any other suggestion is most welcome.
I think I have found a solution. I would appreciate it if others who are interested would try this on their own and report the results with their device models and SDK version.
I have seen similar posts which direct to this but I thought I would post it anyway since it is newer and seems to work on newer versions of the SDK - so far it works on my Nexus One running Android 2.3.6.
The solution relies on bufferring the input stream to a local file (I have this file on the external storage but it will probably be possible to place it on the intenal storage as well) and providing that file's descriptor to the MediaPlayer instance.
The following runs in a doInBackground method of some AsyncTask that does AudioPlayback:
#Override
protected
Void doInBackground(LibraryItem... params)
{
...
MediaPlayer player = new MediaPlayer();
setListeners(player);
try {
_remoteStream = getMyInputStreamSomehow();
File tempFile = File.createTempFile(...);
tempFile.deleteOnExit();
_localInStream = new FileInputStream(tempFile);
_localOutStream = new FileOutputStream(tempFile);
int buffered = bufferMedia(
_remoteStream, _localOutStream, BUFFER_TARGET_SIZE // = 128KB for instance
);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setDataSource(_localInStream.getFD());
player.prepareAsync();
int streamed = 0;
while (buffered >= 0) {
buffered = bufferMedia(
_remoteStream, _localOutStream, BUFFER_TARGET_SIZE
);
}
}
catch (Exception exception) {
// Handle errors as you see fit
}
return null;
}
The bufferMedia method buffers nBytes bytes or until the end of input is reached:
private
int bufferMedia(InputStream inStream, OutputStream outStream, int nBytes)
throws IOException
{
final int BUFFER_SIZE = 8 * (1 << 10);
byte[] buffer = new byte[BUFFER_SIZE]; // TODO: Do static allocation instead
int buffered = 0, read = -1;
while (buffered < nBytes) {
read = inStream.read(buffer);
if (read == -1) {
break;
}
outStream.write(buffer, 0, read);
outStream.flush();
buffered += read;
}
if (read == -1 && buffered == 0) {
return -1;
}
return buffered;
}
The setListeners method sets handlers for various MediaPlayer events. The most important one is the OnCompletionListener which
is invoked when playback is complete. In cases of buffer underrun (due to, say, temporary slow network connection) the player
will reach the end of the local file and transit to the PlaybackCompleted state. I identify those situations by comparing the
position of _localInStream against the size of the input stream. If the position is smaller, then playback is now really completed
and I reset the MediaPlayer:
private
void setListeners(MediaPlayer player)
{
// Set some other listeners as well
player.setOnSeekCompleteListener(
new MediaPlayer.OnSeekCompleteListener()
{
#Override
public
void onSeekComplete(MediaPlayer mp)
{
mp.start();
}
}
);
player.setOnCompletionListener(
new MediaPlayer.OnCompletionListener()
{
#Override
public
void onCompletion(MediaPlayer mp)
{
try {
long bytePosition = _localInStream.getChannel().position();
int timePosition = mp.getCurrentPosition();
int duration = mp.getDuration();
if (bytePosition < _track.size) {
mp.reset();
mp.setDataSource(_localInStream.getFD());
mp.prepare();
mp.seekTo(timePosition);
} else {
mp.release();
}
} catch (IOException exception) {
// Handle errors as you see fit
}
}
}
);
}
Another solution would be to start a proxy HTTP server on localhost. The media player will connect to this server with setDataSource(Context context, Uri uri). This solution works better than the previous and does not cause playback to glitch.
I'm new to Android development and I have a question/problem.
I'm playing around with the MediaPlayer class to reproduce some sounds/music. I am playing raw resources (res/raw) and it looks kind of easy.
To play a raw resource, the MediaPlayer has to be initialized like this:
MediaPlayer mp = MediaPlayer.create(appContext, R.raw.song);
mp.start();
Until here there is no problem. The sound is played, and everything works fine. My problem appears when I want to add more options to my application. Specifically when I add the "Stop" button/option.
Basically, what I want to do is...when I press "Stop", the music stops. And when I press "Start", the song/sound starts over. (pretty basic!)
To stop the media player, you only have to call stop(). But to play the sound again, the media player has to be reseted and prepared.
mp.reset();
mp.setDataSource(params);
mp.prepare();
The problem is that the method setDataSource() only accepts as params a file path, Content Provider URI, streaming media URL path, or File Descriptor.
So, since this method doesn't accept a resource identifier, I don't know how to set the data source in order to call prepare(). In addition, I don't understand why you can't use a Resouce identifier to set the data source, but you can use a resource identifier when initializing the MediaPlayer.
I guess I'm missing something. I wonder if I am mixing concepts, and the method stop() doesn't have to be called in the "Stop" button. Any help?
Thanks in advance!!!
Here is what I did to load multiple resources with a single MediaPlayer:
/**
* Play a sample with the Android MediaPLayer.
*
* #param resid Resource ID if the sample to play.
*/
private void playSample(int resid)
{
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
try
{
mediaPlayer.reset();
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
mediaPlayer.prepare();
mediaPlayer.start();
afd.close();
}
catch (IllegalArgumentException e)
{
Log.e(TAG, "Unable to play audio queue do to exception: " + e.getMessage(), e);
}
catch (IllegalStateException e)
{
Log.e(TAG, "Unable to play audio queue do to exception: " + e.getMessage(), e);
}
catch (IOException e)
{
Log.e(TAG, "Unable to play audio queue do to exception: " + e.getMessage(), e);
}
mediaPlay is a member variable that get created and released at other points in the class. This may not be the best way (I am new to Android myself), but it seems to work. Just note that the code will probably fall trough to the bottom of the method before the mediaPlayer is done playing. If you need to play a series of resources, you will still need to handle this case.
this is how MediaPlayer.create method works to open a raw file:
public static MediaPlayer create(Context context, int resid) {
try {
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
if (afd == null) return null;
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
mp.prepare();
return mp;
} catch (IOException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
} catch (IllegalArgumentException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
} catch (SecurityException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
}
return null;
}
Or, you could access the resource in this way:
mediaPlayer.setDataSource(context, Uri.parse("android.resource://com.package.name/raw/song"));
where com.package.name is the name of your application package
You can use
mp.pause();
mp.seekTo(0);
to stop music player.
Finally, the way it works for me:
public class MainStart extends Activity {
ImageButton buttonImage;
MediaPlayer mp;
Boolean playing = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
buttonImage = (ImageButton)findViewById(R.id.ButtonID);
buttonImage.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(playing){
mp.stop();
playing = false;
}else{
mp = MediaPlayer.create(getApplicationContext(), R.raw.sound_u_want);
mp.start();
playing = true;
}
}
});
}
}
MR. Rectangle, this message maybe too late for it, but I proudly write these codes to your idea: I have mp for mediaplayer and sescal9 is a button.
....
if(btnClicked.getId() == sescal9_ornek_muzik.getId())
{
mp.start();
mp.seekTo(380);
mp2.start();
mp2.seekTo(360);
mp3.start();
mp3.seekTo(340);
...
}
Recheck your passing parameters not null
Possible reasons
Context may be null
Your media file may be corrupted