Best way to pass a SurfaceView to android.media.MediaPlayer - android

I'm writing an app that consumes media (audio/video) and that allows users to reply and/or post new media.
My question relates to the SurfaceView used to display the videos. This SurfaceView object is shared between the MediaRecorder (recording a video) and the MediaPlayer (consuming/playing the video).
The MediaPlayer is located on its own Service, which runs on its own thread, as per the NPR example: PlaybackService.java
Since the NPR example doesn't involve video, I was not sure about how to make the MediaPlayer on the Service aware of the SurfaceView in the UI. I ended up using a static variable to solve this issue:
// MyFragmentClass.java
// CameraPreview is the sample class shown int he media section
// of the Android Developer site:
// http://developer.android.com/guide/topics/media/camera.html#camera-preview
private static CameraPreview sCameraPreview;
#Override
public void onStart() {
super.onStart();
CameraPreview cameraPreview = new CameraPreview(getActivity());
FrameLayout preview = (FrameLayout) getView().findViewById(R.id.video_preview);
preview.addView(cameraPreview);
setCameraPreview(cameraPreview); // a static setter.
// Other classes are initialized afterwards, not relevant to this question...
}
public static CameraPreview getCameraPreview() {
return sCameraPreview;
}
public static void setCameraPreview(CameraPreview cameraPreview) {
sCameraPreview = cameraPreview;
}
Here's the method on the PlaybackService that takes care of preparing a video for playback:
// Params url and isVideo were extras on an Intent that triggers
// this method
synchronized private void prepareMediaPlayer(String url, boolean isVideo) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnInfoListener(this);
mMediaPlayer.setOnPreparedListener(this);
} else {
mMediaPlayer.reset();
}
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(mediaUrl);
if (isVideo) {
// this is the important line
mMediaPlayer.setDisplay(MyFragmentClass.getCameraPreview().getHolder());
mMediaPlayer.setScreenOnWhilePlaying(true);
}
mMediaPlayer.prepareAsync();
sendIntent(STATUS_PLAYBACK_PREPARED);
}
It gets the job done, but I'm wondering if there's any better way to do it, specially because I'm debugging a random bug where the shared surface is not released, and it got me thinking:
Is there a better way to make my service class aware of the Service?
Is it a good approach to have a single SurfaceView for both Recording and Playback?
Thanks!

Related

stream wifi camera to android

I'm trying to display the video stream from my wifi camera (SJ6 Legend) to an Android device.
When turning on the wifi from the camera and connecting to its network from my mac I can see the video stream from vlc simply by going to File -> Open Network and connecting to rtsp://MY_CAM_IP.
I then connect to the wifi from my android device and I tried using MediaPlayer or VideoView and it doesn't work.
vlc for android also doesn't display the video.
Just to make sure there is no problem playing RTSP I tried this file:
rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov
and it works fine on vlc for android and using MediaPlayer.
I also tried a vlc for android lib, that didn't work as well...
Relevant code:
In onCreate:
SurfaceView surfaceView = (SurfaceView)
findViewById(R.id.am_surface_view);
mSurfaceHolder = surfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setFixedSize(320, 240);
and:
/**
* {#link MediaPlayer.OnPreparedListener} interface methods
*/
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mMediaPlayer.start();
}
/**
* {#link SurfaceHolder.Callback} interface methods
*/
#Override
public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {}
#Override
public void surfaceCreated(SurfaceHolder sh) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDisplay(sh);
// Context context = getApplicationContext();
// Map<String, String> headers = getRtspHeaders();
// Uri source = Uri.parse(RTSP_URL);
try {
// Specify the IP camera's URL and auth headers.
// mMediaPlayer.setDataSource(context, source, headers);
// mMediaPlayer.setDataSource(context, source);
mMediaPlayer.setDataSource(RTSP_URL); // RTSP_URL = "rtsp://MY_CAM_IP"
// Begin the process of setting up a video stream.
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.prepareAsync();
} catch (Exception e) {}
}
#Override
public void surfaceDestroyed(SurfaceHolder sh) {
mMediaPlayer.release();
}
Anyone can point me to some solution???
Thanks
It finally worked when I turned off the phones' cellular network data.
Unfortunately I need to receive the camera stream and have a network connection to send the frames received but I guess that's another question...

How to play an mp3 file between a range of milliseconds using android MediaPlayer?

I am able to play an mp3 file using android's MediaPlayer object. But I would like to play between a range of milliseconds for example between 30000 ms to 40000 ms ( 10 seconds only ). How can I achieve this?
Currently the following code is what I have,
private MediaPlayer mPlayer;
public void play() {
try {
mPlayer = MediaPlayer.create(getApplicationContext(), R.raw.mp3_file);
if (mPlayer != null) {
int currentPosition = mPlayer.getCurrentPosition();
if (currentPosition + 30000 <= mPlayer.getDuration()) {
mPlayer.seekTo(currentPosition + 30000);
} else {
mPlayer.seekTo(mPlayer.getDuration());
}
mPlayer.start();
}
}
catch(Exception e) {
}
}
Any help is greatly appreciated. Thank you!
You can use the method:
public int getCurrentPosition ()
to obtain the current time in milSeconds maybe inside a Handler that runs every 1000 milSeconds and tests to see:
if(mPlayer.getCurrentPosition() >= (mPlayer.getDuration + 40000));
Dont forget to release the media file when you're done using it:
public void release();
mPlayer.release();
Releases resources associated with this MediaPlayer object. It is
considered good practice to call this method when you're done using
the MediaPlayer. In particular, whenever an Activity of an application
is paused (its onPause() method is called), or stopped (its onStop()
method is called), this method should be invoked to release the
MediaPlayer object, unless the application has a special need to keep
the object around. In addition to unnecessary resources (such as
memory and instances of codecs) being held, failure to call this
method immediately if a MediaPlayer object is no longer needed may
also lead to continuous battery consumption for mobile devices, and
playback failure for other applications if no multiple instances of
the same codec are supported on a device. Even if multiple instances
of the same codec are supported, some performance degradation may be
expected when unnecessary multiple instances are used at the same
time.
The best approach is to use a Handler to time the stopping of the playback. Start the player and then use the Handler's postDelayed to schedule the execution of a Runnable that will stop the player. You should also start the player only after the initial seek completes. Something like this:
public class PlayWord extends Activity implements MediaPlayer.OnSeekCompleteListener {
Handler mHandler;
MediaPlayer mPlayer;
int mStartTime = 6889;
int mEndTime = 7254;
final Runnable mStopAction = new Runnable() {
#Override
public void run() {
mPlayer.stop();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TextView tv = new TextView(this);
tv.setText("Playing...");
setContentView(tv);
mHandler = new Handler();
mPlayer = MediaPlayer.create(this, R.raw.nicholas);
mPlayer.setOnSeekCompleteListener(this);
mPlayer.seekTo(mStartTime);
}
#Override
public void onDestroy() {
mPlayer.release();
}
#Override
public void onSeekComplete (MediaPlayer mp) {
mPlayer.start();
mHandler.postDelayed(mStopAction, mEndTime - mStartTime);
}
}
Note also that the MediaPlayer.create method you are using returns a MediaPlayer that has already been prepared and prepare should not be called again like you are doing in your code.on the screen. I also added a call to release() when the activity exits.
Also, if you want to update the UI when the seek completes, be aware that this method is usually called from a non-UI thread. You will have to use the handler to post any UI-related actions.
I'm copied this from: Android: How to stop media (mp3) in playing when specific milliseconds come?

MediaPlayer gives different results if it has to wait longer before .start() is called

I'm trying to use a MediaPlayer instance to play several audio files individually, in response to various sensor events.
I've found that when I load up the clip to be played right before calling MediaPlayer.start(), the audio clip will play fine. However, the application takes a major performance hit. Ideally, each audio clip should be loaded into the MediaPlayer immediately after the last one was played, leaving the MediaPlayer ready to start playback the instant the SensorEvent comes in.
I would expect this to be simple, but now that I made the change the audio just doesn't play. PlayAudioClip() is definitely still being called as expected, but something is going wrong after that. No errors are thrown, so I don't think the MediaPlayer is changing state, but could something be interfering with in the time that it's waiting to play?
Here is a simplified version of my code:
public class MainActivity extends Activity implements SensorEventListener {
private Random numGenerator;
private SensorManager manager;
private Sensor accelerometer;
private MediaPlayer mediaPlayer;
private Uri[] audioClips;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initVariables();
prepareNextAudioClip(); //load first audioClip
}
#Override
public void onSensorChanged(SensorEvent event) {
if(conditionsRight()){
playAudioClip();
}
}
}
private void playAudioClip() {
mediaPlayer.start();
prepareNextAudioClip();
}
private void prepareNextAudioClip() {
try {
mediaPlayer.reset();
Uri audioClip = audioclips[(int) Math.floor(numGenerator.nextDouble()*audioClips.length)];
mediaPlayer.setDataSource(this, audioClip);
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
//Code below here isn't very important... handling setup and teardown
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
protected void onResume() {
super.onResume();
manager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
}
private void initVariables() {
audioClips = new Uri[]{
Uri.parse("android.resource://com.example.afraidofflying/" + R.raw.audio1),
Uri.parse("android.resource://com.example.afraidofflying/" + R.raw.audio2),
Uri.parse("android.resource://com.example.afraidofflying/" + R.raw.audio3)
};
numGenerator = new Random();
mediaPlayer = new MediaPlayer();
manager = (SensorManager)getSystemService(SENSOR_SERVICE);
accelerometer = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if(null == accelerometer) finish();
}
protected void onPause() {
super.onPause();
manager.unregisterListener(this);
}
protected void onDestroy(){
mediaPlayer.release();
mediaPlayer = null;
}
}
PS: This has all been assuming I'll only use one instance of MediaPlayer but I'd also like input on if you think using multiple MediaPlayers and delegating each of them 1 audio clip would be advisable. My intuition is no because for my purposes I'd have to use 10-20 MediaPlayers, but it would be good to hear outside perspectives on it.
It's because you're resetting player right after starting playback.
private void playAudioClip() {
mediaPlayer.start(); //starting playback
prepareNextAudioClip(); //reset
}
if you want to play files in queue, than you can use one instance. But if you have to play several files simultaneusly, then you need to have several media player instances.
I think you have to look at subtle points regarding using Mediaplayer class
In your code you used:
initVariables();
prepareNextAudioClip(); //load first audioClip
initVariables() seems ok, Now lets see prepareNextAudioClip()
...
mediaPlayer.reset();
...
...
mediaPlayer.prepare();
The above code seems to corrupt Mediaplayer state machine. Please refer to http://developer.android.com/reference/android/media/MediaPlayer.html for details on using new, prepare,reset. It is better to write defensive MediaPlayer code using Errorlistener

Want to package a small video in the package for deployment

Is there any way to reference programmatically a very small video file adn include it in teh package - i.e. I don't want to have it separate on the SD card. I am thinking of putting it in the 'raw' package directory.
E.g. MPEG4 called 'video' in 'raw'
Am trying to work out what the correct format for Uri.parse() but it has beaten me. I thought it should be something like R.raw (as used when setting up a media player for audio myMediaPlayer = MediaPlayer.create(this, R.raw.audiocameralive1) - but it doesn't seem to be.
Any suggestions
Oliver
I see there have been a number of views, so in case anyone is looking for a solution, this is what I eventually did - and seems to work fine. There is probably cleaner way of doing the same but, this one makes sense to me ...
Oliver
public class ShowVideoActivity extends Activity
implements SurfaceHolder.Callback,
OnErrorListener,
OnPreparedListener,
OnCompletionListener
{
/** Called when the activity is first created. */
private MediaPlayer myMediaPlayer;
boolean bolMediaPlayerIsReleased = false;
// The SurfaceHolder and SurfaceView are used to display the video
// By implementing the SurfaceHolder.Callback interface means that we have
// to implement surfaceChanged(), surfaceCreated() and surfaceDestroyed()
private SurfaceView videoSurface;
private SurfaceHolder videoHolder;
Display currentDisplay;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.showvideo); // Inflate ShowVideo
// Identify the Surface that will be used to hold the camera image
videoSurface = (SurfaceView)findViewById(R.id.videosurface);
// The SurfaceHolder 'monitors' activity on the Surface
videoHolder = videoSurface.getHolder();
videoHolder.setKeepScreenOn(true);
// Data will be Pushed onto the buffers external to the surface
videoHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
videoHolder.setKeepScreenOn(true);
// Let the monitor know that 'this' activity is responsible for
// all the callback functions.
videoHolder.addCallback(this);
// It is now up to the 'callbacks' to do any further processing
myMediaPlayer = MediaPlayer.create(this,R.raw.filename);
myMediaPlayer.setOnCompletionListener(this);
myMediaPlayer.setOnErrorListener(this);
myMediaPlayer.setOnPreparedListener(this);
myMediaPlayer.setOnCompletionListener(this);
currentDisplay = getWindowManager().getDefaultDisplay();
}
// Set up a listener to wait for MediaPlayer End (Is this PlaybackCompleted()?)
public void onCompletion(MediaPlayer mp)
{
Wrapup(mp);
}
public void surfaceCreated(SurfaceHolder CreatedHolder) {
// Surface created, now it is possible to set the preview
myMediaPlayer.setDisplay(CreatedHolder);
}
public void surfaceDestroyed(SurfaceHolder DestroyedHolder)
{
if (myMediaPlayer != null)
{
if (myMediaPlayer.isPlaying() )
myMediaPlayer.stop();
myMediaPlayer.release();
bolMediaPlayerIsReleased = true;
}
}
public void surfaceChanged(SurfaceHolder ChangedHolder, int intFormat, int intWidth, int intHeight)
{
if (myMediaPlayer.isPlaying())
return;
else
{
setVideoSurfaceSize(myMediaPlayer);
myMediaPlayer.start();
}
}
public boolean onError(MediaPlayer mPlayer, int intError, int intExtra)
{
return false;
}
public void onPrepared(MediaPlayer mPlayer)
{
setVideoSurfaceSize(mPlayer);
mPlayer.start();
// From the 'Started' mode, the player can either be 'Stopped', 'Paused' or PlaybackCompleted'
} // End onPrepared
public void Wrapup(MediaPlayer mp)
{
if (mp != null)
{
if (myMediaPlayer.isPlaying() )
mp.stop();
mp.release();
bolMediaPlayerIsReleased = true;
}
// Now clean up before terminating. This is ESSENTIAL
// If cleanup is NOT done then the surfaceDestroyed will get called
// and screw up everything
// Firstly remove the callback
videoHolder.removeCallback(this); // Prevents callbacks when the surface is destroyed
ShowVideoActivity.this.finish();
}
}
Use Activity.getAssets() to get an AssetManager. The load the file with open.

Android MediaPlayer - only one instance at any given time?

I have a audio player app, where there is a Main activity that shows 3 audio sample urls. On click on one, it goes to a Details Activity, which has a play and pause button, to start and pause the audio.
My problem is that, when I start the Main activity, and say click on audio 1, I hit play on Details activity. This starts the MediaPlayer and the audio starts to play. When I go back to the Main activity, the audio is still playing, which is what I want. Now, when I click on audio 1 again, and go to Details Activity and hit play again, there seems to be a new MediaPlayer starting the audio. So I have 2 tracks playing together!
Is there a way I can have only one MediaPlayer instance at any given time?
Thanks
Chris
You should consider the Singleton pattern. Make a class MyPlayer that has a static method getMediaPlayer() that returns the same instance of MediaPlayer each time called.
Singleton Class
public final class MySingleton extends Application {
static MediaPlayer instance;
public static MediaPlayer getInstance() {
if (instance == null)
{
instance = new MediaPlayer();
}
return instance;
}
}
Adapter Where your List
Initialize Your Singleton Class One time in Constructor
private static MediaPlayer mMediaPlayer = MySingleton.getInstance();
if (mMediaPlayer.isPlaying()) {
try {
mMediaPlayer.reset();
mMediaPlayer.stop();
mMediaPlayer.setDataSource(mainActivity, Uri.parse(songsarraylist.get(position).getPath()));
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (Exception e) {
Toast.makeText(mainActivity, "Catching", Toast.LENGTH_SHORT).show();
}
} else {
try {
mMediaPlayer.setDataSource(mainActivity, Uri.parse(songsarraylist.get(position).getPath()));
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}

Categories

Resources