Edit: My issue not solved yet!
Hello stackoverflow friends.
I wrote a video player which can play my recorded videos. But there was rotating 90 degree problem when showing. I used from mediaRecorder.setOrientationHint(int degree) ,but I guess My video player ignores the composition matrix in video during palyback( according to setOrientationHint documentation),Because this method hasn’t any effect on rotation. Finally I decided directly in my video player editing surfaceView by rotating canvas matrix. But this way has this issue which cant show my video in surface view. Only I can hear voice without any showing! How can I resolve this issue?
If there are other ways for rotating my video in surfaceView I want know about those ways. I searched a lot for rotating but I couldnt get any success.
This is most importants piece of my code.
public class VideoPlayerActivity extends Activity implements
SurfaceHolder.Callback
{
private SurfaceView mPreview;
private SurfaceHolder holder;
private MediaPlayer mPlayer;
//-------------------------------------------
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.video_player);
InitializeUI();
}
//------------------------------------------
private void InitializeUI()
{
mPreview = (SurfaceView) findViewById(R.id.surface_view);
holder = mPreview.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
}
//------------------------------------------
private void playVideo(Integer Media)
{
try {
Canvas canvas=holder.lockCanvas();
Matrix mat=canvas.getMatrix();
mat.postRotate(90);
canvas.setMatrix(mat);
holder.unlockCanvasAndPost(canvas);
mPlayer.reset();
mPlayer.setDataSource(ArrayManager.array_for_playing.get(SDcard.icurrentIndex).getPath());
mPlayer.setDisplay(holder);
mPlayer.prepare();
mPlayer.start();
}
catch (Exception e)
{
Log.e("video player", "error: " + e.getMessage(), e);
}
}
//------------------------------------------
#Override
public void surfaceCreated(SurfaceHolder holder)
{
if(ArrayManager.array_for_playing != null)
if(SDcard.icurrentIndex != -1)
playVideo(SDcard.icurrentIndex);
}
.
.
.
}
You have to recreate your Media Player (mPlayer).
Just reuse your holder
Related
I am creating an android activity that will have many mediaplayers inside. The activity will have many mediaplayer objects and if i could describe it its like a
4 X 1 grid of media players. I created the 4X1 grid by using TextureView class's in android.
so the xml layout for the activity looks like this more or less:
<TextureView
android:id="#+id/surface_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextureView
android:id="#+id/surface_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextureView
android:id="#+id/surface_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextureView
android:id="#+id/surface_four"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
So each textureView will be the surface where the MediaPlayers will play there video.
programatically i've set up SurfaceTextureListener's so that i know when the surface is available like this for each one:
TextureView tv1 = (TextureView)findViewById(R.id.surface_one);
tv1.setSurfaceTextureListener(new SurfaceListener_1());
and likewise for the rest. here is how i would do the second texture view setup:
TextureView tv2 = (TextureView)findViewById(R.id.surface_two);
tv2.setSurfaceTextureListener(new SurfaceListener_2());
Then in code i am creating 4 MediaPlayer Objects and setting there surface like this for mediaplayer #1:
private class SurfaceListener_1 implements TextureView.SurfaceTextureListener {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Surface sfc = new Surface(surface);
try {
m_mp1 = new MediaPlayer();
m_mp1.setSurface(sfc); //critical , here i am adding the surface so the media player can play on it
m_mp1.setDataSource(m_context, m_videoUri_one);
m_mp1.prepareAsync();
m_mp1.setLooping(true);
m_mp1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
video1Prepared = true;
}
});
m_mp1.setOnErrorListener(new MediaPlayer.OnErrorListener(){
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return true;
}
});
} catch (Exception e) {
e.printStackTrace();
}
finally {
//release the surface as its now set in the media player
if(sfc!=null)
sfc.release();
}
}
//...... the rest of the call backs not important...
and for the other media players i repeat the same thing so that they all have surfaces to play on. here is how i would do mediaplayer # 2:
private class SurfaceListener_2 implements TextureView.SurfaceTextureListener {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Surface sfc = new Surface(surface);
try {
m_mp2 = new MediaPlayer();
m_mp2.setSurface(sfc); //critical , here i am adding the surface so the media player can play on it
m_mp2.setDataSource(m_context, m_videoUri_two);
m_mp2.prepareAsync();
m_mp2.setLooping(true);
m_mp2.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
video2Prepared = true;
}
});
m_mp2.setOnErrorListener(new MediaPlayer.OnErrorListener(){
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return true;
}
});
} catch (Exception e) {
e.printStackTrace();
}
finally {
//release the surface as its now set in the media player
if(sfc!=null)
sfc.release();
}
}
//...... the rest of the call backs not important...
so in the end what i have created is 4 surfaces where i can play a video. All 4 media players play different video files.
Once all the media players are prepared, i call their start() method on all of them and they play at the same time.
The issue i am facing is that this can be cpu intensive. On some devices i can get a ANR or the app just gets slow to respond.
The media file i am playing is a MP4 and the size of the media files are all about 9 MB.
Is there anything you can recommend so that i can be more cpu efficient in this already heavy task ? For example, can i get the GPU to help ?
Or if i change media type will that make it more efficient ? the video's do not have sound there only visual if that helps.
I want to see the video that I'm currently recording in a SurfaceView and if the recodring finished I wanto to replay the video in the same SurfaceView.
Even though I thought it would be a common feature in Apps, I couldn't find any example and couldn't make my code work.
private void prepareRecorder() {
try {
recorder = new MediaRecorder();
recorder.setPreviewDisplay(holder.getSurface());
camera.unlock();
recorder.setCamera(camera);
recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
camcorderProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
recorder.setProfile(camcorderProfile);
File newFile = new File(videoFilePath);
recorder.setOutputFile(newFile.getAbsolutePath());
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}catch(Exception e){
releaseCamera();
}
}
//
public void onClick(View v) {
if(mediaPlayer!=null) {
mediaPlayer.reset();
mediaPlayer.release();
}
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(videoFile.getAbsolutePath());
SurfaceView surfaceView = (SurfaceView) getView().findViewById(R.id.videoInvitationFragSurfaceView);
mediaPlayer.setSurface(surfaceView.getHolder().getSurface());
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
If I just record the video my code works, if just play the video it works as well. Any ideas?
Thanks in advance
Edit: As a current workaround I use two SurfaceViews and "hide" them (set size to 0) if needed
If I just record the video my code works, if just play the video it works as well. Any ideas?
As much I understand, you can reuse same surface view only after it is destroyed and regenerated, i.e you can reuse same SurfaceView only after surfaceDestroyed() is called and then surfaceCreated() is called only after this destroy and create cycle you can reuse SurfaceView.
To achive this all you have to do is: after video recording is done set SurfaceView visibility to GONE which will ensure surfaceDestroyed() call back and again set SurfaceView visibility to VISIBLE which will call surfaceCreated() after that start video playback.
This is very cumbersome process so I used same SurfaceView for recording and playback in following way:
when Activity is created check if VideoFile is available in Intent
if yes use SurfaceView for playback
now if user deletes video restart activity
As VideoFile is not found in intent use SurfaceView for recording
Once recording is done and user want to playback video restart
Activity with VideoFile information attached in Intent
I have a custom viewgroup (Custom2) inside a custom viewgroup (Custom1).
Custom1 has an imageview displaying a RectShape Shapedrawable and two textviews.
I set an onClickListener in Custom2 which removes the imageview using this.removeViewAt(0) and creates a new TextureView, then calling this.addView (mTextureView, 0) and finally invalidate. I then get the textureview to play a video using mediaplayer.
Here is the code:
public void setToTextureView() {
//TextureView when not debugging
TextureView View;
View = (TextureView) new TextureView(mContext);
View.setLayoutParams(new LayoutParams(600, 500));
this.removeViewAt(0);
this.addView(View, 0);
View.invalidate();
Log.d("Debug", "Invalidate Called");
View.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Surface mSurface = new Surface(surface);
MediaPlayer mMediaPlayer = new MediaPlayer(); //Debug MediaPlayer issues by setting ItemToDebug = "MediaPlayer"
try {
mMediaPlayer.setDataSource(mContext, Uri.parse("android.resource://" + mContext.getPackageName() + "/" + R.raw.t));
if (ItemToDebug.equals("MediaPlayer")) {
Log.d("MediaPlayer", "Data source set");
}
mMediaPlayer.setSurface(mSurface);
if (ItemToDebug.equals("MediaPlayer")) {
Log.d("MediaPLayer", "Surface set");
}
mMediaPlayer.prepare();
if (ItemToDebug.equals("MediaPlayer")) {
Log.d("MediaPLayer", "Prepared");
}
} catch (IOException e) {
Log.d("IOException", e.getMessage());
}
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if (ItemToDebug.equals("MediaPlayer")) {
Log.d("Debug", "Media Player Prepared");
}
mp.start();
}
});
}
Now, I notice bizarrely that the ImageView disappears and an empty space appears (of the correct size) when I click the view, but no video plays, although the code appears to execute fine upto invalidate();
However, I can then get the TextureView to get a surfaceTexture and play the video when I interrupt the program and go back in (i.e by pressing the button that takes you to the screen with all the open programs, and selecting my activity again.)
I don't have a good understanding of exactly what is happening but I am pretty sure that no SurfaceTexture is being created because onAttached() isn't being called.
Does anybody know what might be going on?
Cheers,
J
The view disappears, I need to call requestLayout() in order to draw something else there.
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!
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.