Xamarin android : VideoView does not call SurfaceCreated - android

I'm struggling to play a video from Assets with a VideoView and a MediaPlayer.
Build version : android 6.0.1 Marshmallow API 23
Debug Device : NVIDIA SHIELD Tablet K1
The issue : my VideoView seems to not create a surface, since the function "SurfaceCreated" is never called during debug.
Actual result : when the activity is loaded, the screen is grey and I can hear the correct sound of the video.
Here is my code :
public class VideoTestActivity : Activity, MediaPlayer.IOnPreparedListener, ISurfaceHolderCallback
{
MediaPlayer player;
VideoView _myVideoView;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.VideoTest);
player = new MediaPlayer();
_myVideoView = FindViewById<VideoView>(Resource.Id.video);
_myVideoView.Start();
_myVideoView.SetZOrderOnTop(true); // tried from Q&A, display a grey screen
var descriptor = Assets.OpenFd("video.mp4");// the data is correctly loaded
player.SetDataSource(descriptor.FileDescriptor, descriptor.StartOffset, descriptor.Length);
player.Prepare();
player.Start();
}
public void OnPrepared(MediaPlayer mp)
{
throw new NotImplementedException();
}
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
{
throw new NotImplementedException();
}
public void SurfaceCreated(ISurfaceHolder holder)
{
player.SetDisplay(_myVideoView.Holder); // never called
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
throw new NotImplementedException();
}
}
And here is my axml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:p1="http://schemas.android.com/apk/res/android"
p1:minWidth="25px"
p1:minHeight="25px"
p1:layout_width="match_parent"
p1:layout_height="match_parent"
p1:id="#+id/relativeLayout1">
<VideoView
p1:layout_width="fill_parent"
p1:layout_height="fill_parent"
p1:id="#+id/video"/>
</RelativeLayout>
What I have tried/checked :
my VideoView has a widht/height different from 0
the descriptor is correctly loaded
SetZOrderOnTop() has been called after Start()
the videoView is correctly loaded by FindViewById()
I'm still searching for an anwser, but since I have checked every post-related on stackOverflow, I opened this post.
Thanks for your time and knowledge.

Ok so finally i found a solution to play my video, but none to load the Surface.
Creating a raw folder was the solution.
here is the new code (other fields from the previous one are blank):
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.VideoTest);
VideoView view = (VideoView)FindViewById(Resource.Id.videoPacificRim);
String path = "android.resource://" + PackageName + "/" + Resource.Raw.pacificrim_file;
view.SetVideoURI(Android.Net.Uri.Parse(path));
view.Start();
}
I close the subject, even if the original question is not answered.

Related

android app with multiple media players in same activity - how to use efficiently

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.

How to set the preview image in videoview before playing

I created an VideoView in my activity, below is the code.
VideoView vvVideos = (VideoView) rootView.findViewById(R.id.videoView);
MediaController mediacontroller = new MediaController(ctx);
mediacontroller.setAnchorView(vvVideos);
Uri video = Uri.parse("android.resource://" + packageName +"/"+R.raw.sample);
vvVideos.setMediaController(mediacontroller);
LayoutParams params=vvVideos.getLayoutParams();
params.height=150;
vvVideos.setLayoutParams(params);
vvVideos.setVideoURI(video);
vvVideos.requestFocus();
vvVideos.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
vvVideos.start();
}
});
Now the video gets started to play when the activity gets created. I want to make my activity as follows
Video should not play when the activity gets open.
It shoud display the starting video image(currently its displaying black color)
It should play only when the user click on the video.
please help me.
Use seekTo( 1 ) to show the first frame.
Ensure the movie is paused and then use seekTo() to show the first frame of the video:
VideoView mVideoView = (VideoView) findViewById( R.id.video_preview );
mVideoView.setVideoURI( yourVideoPath );
mVideoView.seekTo( 1 ); // 1 millisecond (0.001 s) into the clip.
NOTE: We use .seekTo( 1 ) because setting .seekTo( 0 ) did not work on Android 9.
To have it play when clicked on has been answered by #Lingviston in another answer.
Create video thumbnail using this
Bitmap thumb = ThumbnailUtils.createVideoThumbnail("file path/url",
MediaStore.Images.Thumbnails.MINI_KIND);
and set to videoview
BitmapDrawable bitmapDrawable = new BitmapDrawable(thumb);
mVideoView.setBackgroundDrawable(bitmapDrawable);
1) Remove your onPrepareListener. I don't know why your video is starting playing after activity creation but onPrepareListener is called after videoView.start().
2) Add an ImageView widget into you layout on top of VideoView. Then set another onPrepareListener like this:
vvVideos.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
previewImage.setVisibility(View.GONE);
}
});
I've noticed that onPreparedListener fires too early, so you can use
new Handler().postDelay(Runnable, timeInMilis)
to dismiss preview image.
3) Add OnTouchListener with any gesture detection to you VideoView. Here is an example of what I'm using now:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
mGestureDetector = new GestureDetector(this, mGestureListener);
((VideoView) findViewById(R.id.activity_video_videoview)).setOnTouchListener(mTouchListener);
}
private OnTouchListener mTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mGestureDetector.onTouchEvent(event);
return true;
}
};
private SimpleOnGestureListener mGestureListener = new SimpleOnGestureListener() {
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if(mVideoView.isPlaying())
mVideoView.pause();
else
mVideoView.start();
return true;
};
};
It starts/stops playing by a tap.
just seek video to 100 milliseconds it shows thumbnail using seekTo() method
videoView.seekTo(100);
You can do it with Glide 4.x. It will fetch the first frame of your video show it in an ImageView
add to your build.gradle
implementation 'com.github.bumptech.glide:glide:4.x.x'
and in your Class
GlideApp.with(context)
.load("your video URL")
.into(videoImageView);;
Thought I'd share my solution. The seekTo method works great but only for some devices. Here is my work around. I handle this in the onPrepared method for the onPreparedListener but its up to you.
#Override
public void onPrepared(MediaPlayer mp) {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(uri.getPath());
try {
Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(currentPosition);
videoView.setBackgroundDrawable(new BitmapDrawable(bitmap));
} catch (OutOfMemoryError outOfMemoryError) {
//Not entirely sure if this will ever be thrown but better safe than sorry.
videoView.seekTo(currentPosition);
}
}
Now when you play the video you will need to remove this background image like so:
private void play() {
...
videoView.setBackgroundDrawable(null);
..
}
Enjoy!
I know it's an old question, but I needed the same solution and couldn't find the answer anywhere else so I did the solution and I'm sharing the love here:
I just created a little class for it:
public class LoadFirstVideoFrame implements Runnable {
private static Handler uiHandler = new Handler(Looper.getMainLooper());
private VideoView videoView;
private LoadFirstVideoFrame(VideoView videoView) {
this.videoView = videoView;
videoView.start();
videoView.resume();
uiHandler.post(this);
}
public void stop() {
videoView = null;
uiHandler.removeCallbacks(this);
}
#Override public void run() {
if (videoView == null) return;
if (videoView.isPlaying()) {
videoView.pause();
videoView.seekTo(0);
videoView = null;
} else {
uiHandler.post(this);
}
}
}
it simply asks to start playing and pauses the video back on the first frame as soon as it's actually playing (meaning it loaded the file and it's already rendering it on the SurfaceView).
The important note here is to remember to call stop() on this class so it can properly clean up and not memory leak anything.
I hope it helps.
I Have Created A ImageView For Thumbnail Like This
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:clickable="true"
android:focusable="true">
<VideoView
android:id="#+id/video_view"
android:layout_width="match_parent"
android:layout_height="220dp"/>
<ImageView
android:id="#+id/videoView_thumbnail"
android:layout_width="match_parent"
android:layout_height="220dp"
android:scaleType="centerCrop"/>
</RelativeLayout>
And Add setOnPreparedListener and setOnCompletionListener in Java Like This Way
VideoView videoView = (VideoView) view.findViewById(R.id.video_view);
ImageView thumbnailView=(ImageView)view.findViewById(R.id.videoView_thumbnail);
Glide.with(getApplicationContext()).load(your_Image_Path).into(thumbnailView);
//you can add progress dialog here until video is start playing;
mediaController= new MediaController(getContext());
mediaController.setAnchorView(videoView);
videoView.setMediaController(mediaController);
videoView.setKeepScreenOn(true);
videoView.setVideoPath(your_video_path);
videoView.start(); //call this method for auto playing video
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
thumbnailView.setVisibility(View.GONE);
//you can Hide progress dialog here when video is start playing;
}
});
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
videoView.stopPlayback();
thumbnailView.setVisibility(View.VISIBLE);
}
});
It Works For Me Very Well I Hope Its Usfull For All
very simple !!!
you can use glide library.
first add an imageView on videoView and use the code below:
GlideApp.with(getApplicationContext()).load("empty")
.thumbnail(GlideApp.with(getApplicationContext()).load("videoURL"))
.into(imageView);
this code will download an image, and eventually, that leads to less internet consumption.
You can use a #Joshua Pinter's answer to solve the problem. But I want to give you more suggestion about it. You yourself answer that seekTo(100) works instead of seekTo(1). Neither of the two ways is perfect. That is because seekTo(1) would get a black image and the seekTo(100) may got an exception.
I prefer to do like this:
// calculate the during time of the media
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
long time = mediaPlayer.getDuration();
// use a proper number
mVideoView.seekTo(time/2);
If you don't have the backend server, it may be better. Or, if you had a backend server, you can try it in this way.
The thumbnail is calculate by the server, the client just display the image which the backend response. And when it come to server side, you can do a lot of things.
Which picture is better? What if the picture is black too?
Should the server side generate it? Or should the server side just cache the picture of the media?
If you want to discuss more about the two questions we can discuss them later.
Add this in your xml
`<RelativeLayout
android:layout_width="match_parent"
android:layout_height="#dimen/dimen180"
android:background="#color/color_bg">
<com.google.android.exoplayer2.ui.PlayerView
android:id="#+id/epPlayer"
android:layout_width="match_parent"
android:visibility="gone"
android:layout_height="match_parent"/>
<FrameLayout
android:id="#+id/flTv"
android:layout_width="match_parent"
android:background="#color/color_bg"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/ivTv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/tv"/>
</FrameLayout>
<ProgressBar
android:id="#+id/pbVideoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_centerInParent="true"
android:indeterminate="true" />
</RelativeLayout>
`
for kotlin users
declare variable
private var simpleExoPlayer: ExoPlayer? = null
use this
simpleExoPlayer = SimpleExoPlayer.Builder(this).build()
epPlayer.player = simpleExoPlayer
val dataSourceFactory = DefaultDataSourceFactory(
this,
Util.getUserAgent(this, getString(R.string.app_name))
)
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(it1))
simpleExoPlayer!!.prepare(videoSource)
simpleExoPlayer!!.playWhenReady = true
simpleExoPlayer!!.addListener(object : Player.EventListener{
override fun onPlayerStateChanged(
playWhenReady: Boolean,
playbackState: Int
) {
if(playbackState== Player.STATE_READY)
{
pbVideoView.visibility= View.GONE
epPlayer.visibility= View.VISIBLE
flTv.visibility= View.GONE
}
if (playbackState== Player.STATE_BUFFERING)
{
pbVideoView.visibility= View.VISIBLE
}
}
})
I hope it helps.
To make your video stop playing when the activity starts just remove the video.start() method.

VideoView is not displayed on a Fragment

I have a problem in running a video in Samsung S3(Android 4.1.1), the issue seems to be because the videoview is on a fragment because if I put it on and activity, it works.
Also I found out that if I turn on the GPU hardware acceleration on, the video works.
I have also a game made by drawing on a SurfaceView and that view doesn't work as well(only with GPU on)... The rest of the app content is displayed as it supposed to (buttons and other layouts).
I tested the app on Nexus S and on the emulator and it works fine, also on other devices..
Does anyone know what the problem could be?
Thank you!
And here is the code:
public class VideoFragment extends Fragment implements MediaPlayer.OnCompletionListener,
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener {
private Video mVideo;
private VideoView mVideoView;
// The video position
private int mPosition;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.screen_video, container, false);
mVideoView = (VideoView) fragmentView.findViewById(R.id.VideoView);
return fragmentView;
}
#Override
public void onPause() {
super.onPause();
// Pause the video if it is playing
if (mVideoView.isPlaying()) {
mVideoView.pause();
}
// Save the current video position
mPosition = mVideoView.getCurrentPosition();
}
#Override
public void onResume() {
super.onResume();
mVideoView.setOnCompletionListener(this);
mVideoView.setOnPreparedListener(this);
mVideoView.setOnErrorListener(this);
mVideoView.setKeepScreenOn(true);
// Initialize the media controller
MediaController mediaController = new MediaController(getActivity());
mediaController.setMediaPlayer(mVideoView);
// Set-up the video view
mVideoView.setMediaController(mediaController);
mVideoView.requestFocus();
mVideoView.setVideoPath(mVideo.getUrl());
if (mVideoView != null) {
// Restore the video position
mVideoView.seekTo(mPosition);
mVideoView.requestFocus();
}
}
#Override
public void onDestroy() {
super.onDestroy();
// Clean-up
if (mVideoView != null) {
mVideoView.stopPlayback();
mVideoView = null;
}
}
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
Log.e("VIDEO PLAY", "end video play");
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
// Start the video view
mediaPlayer.start();
Log.e("VIDEO PLAY", "video ready for playback");
}
#Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
Log.e("VIDEO PLAY", "error: " + i);
return true;
}
}
I don't think it's something related to context(Application or Activity).. because on all other devices the Video and the games are displayed..
Thanks for the help!
I have a similar problem, and i solved it changing:
MediaController mediaController = new MediaController(getActivity().getApplicationContext());
to this (In a fragmet):
MediaController mediaController = new MediaController(getActivity());
Hope this helps,...
EDIT
Look at this class
http://code.google.com/p/sinaweibo-honeycomb/source/browse/branches/sinaweibo-merged/src/com/lenovo/dll/SinaWeibo/VideoFragment.java?r=71
If hardware acceleration fixes your issue then I would enable it for that view/window on that device.
In general I've found that when code works on one device but not another it is typically caused by one of the following problems:
Bug in the manufacturer's API implementation
Different interpretation of the API by the manufacturer
Concurrency problem (e.g. race condition, improper synchronization) in your own code that happens to trigger more frequently on a particular device
As far as I can tell you seem to be using the UI thread appropriately so I would imagine your issue falls into one of the first two categories and you'll just need to work around it.
Move MediaController mediaController = new MediaController(getActivity()) from onResume() to onCreateView.

Android VideoView black screen

I have been looking for a way to get rid of the nasty black initial screen on a VideoView before the start() method is run.
I have tried with background image on the widget but it doesn't work as expected at all.
I have also tried putting an image of the first frame in the video on top of the VideoView and hiding it after the start() method.
Adding an onPrepared listener to start the video and then hide the image. This works but there is a horrible flicker in the transition and I don't know how to get rid of it.
Adding the MediaController had no effect at all. The problem persists (I still see the black flicker) and I don't want to have the video controls visible at all.
My code looks like this:
VideoView vSurface= (VideoView) findViewById(R.id.surfaceView1);
vSurface.setVideoURI(Uri.parse("android.resource://com.mypackage/" + R.raw.video1));
vSurface.setVisibility(View.VISIBLE);
vSurface.setOnPreparedListener(this);
vSurface.setDrawingCacheEnabled(true);
vSurface.setOnErrorListener(this);
I meet the same problem, and solve it with the accepted solution above plus this:
#Override
public void onPrepared(MediaPlayer mp) {
mp.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.d(TAG, "onInfo, what = " + what);
if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
// video started; hide the placeholder.
placeholder.setVisibility(View.GONE);
return true;
}
return false;
}
});
I think onPrepared just means the video is ready to play, but not means video started playing. If hide placeholder in onPrepared, the screen still show a black screen.
On my Note3 and Nexus, this solution works well.
I had the same problem on Galaxy tab 2, Android 4.1.1.
Do videoView.setZOrderOnTop(true); and next videoView.start()
It works fine for me.
I got the same problem and i found a solution. Its a little bit hacky but it do the trick.
So basically you need to put your VideoView into a FrameLayout.
Over the videoview you need to add another FrameLayout with the background of your video and when your video is loaded and ready to play you hide the placeholder.
<FrameLayout
android:id="#+id/frameLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:layout_marginTop="50dip" >
<VideoView
android:id="#+id/geoloc_anim"
android:layout_width="fill_parent"
android:layout_height="172dip" android:layout_gravity="top|center" android:visibility="visible"/>
<FrameLayout
android:id="#+id/placeholder"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="#drawable/fondvert_anim">
</FrameLayout>
In your activity you need to implements OnPreparedListener and add this
//Called when the video is ready to play
public void onPrepared(MediaPlayer mp) {
View placeholder = (View) findViewById(R.id.placeholder);
placeholder.setVisibility(View.GONE);
}
So when the video is ready we hide our placeholder and that trick avoid the black flicker screen.
Hope this help someone.
I had same problem and this has worked for me ..
When you want to show video, make videoView.setZOrderOnTop(false); and when you want to hide video, just make videoView.setZOrderOnTop(true);
I 've got same problem I just used videov.setBackgroundColor(Color.WHITE) and then onprepare i used Color.TRANSPARENT) white is still better than black for me
None of the Above worked for me.
In my case, onPrepared gets called BEFORE the black frame went away, so I would still see the black frame.
I needed a solution where the video appeared shortly after the first frame.
So what I did was set the VideoView alpha to 0 in xml:
android:alpha="0"
and then before I start the video I animate the alpha back to 1:
videoView.animate().alpha(1);
videoView.seekTo(0);
videoView.start();
alternatively, you can just post a delayed Runnable to set the alpha to 1, instead of animating it.
By extending a TextureView, I get no black screens in the beginning or end. This is if you want to avoid using ZOrderOnTop(true).
public class MyVideoView extends TextureView implements TextureView.SurfaceTextureListener {
private MediaPlayer mMediaPlayer;
private Uri mSource;
private MediaPlayer.OnCompletionListener mCompletionListener;
private boolean isLooping = false;
public MyVideoView(Context context) {
this(context, null, 0);
}
public MyVideoView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setSurfaceTextureListener(this);
}
public void setSource(Uri source) {
mSource = source;
}
public void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) {
mCompletionListener = listener;
}
public void setLooping(boolean looping) {
isLooping = looping;
}
#Override
protected void onDetachedFromWindow() {
// release resources on detach
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
super.onDetachedFromWindow();
}
/*
* TextureView.SurfaceTextureListener
*/
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Surface surface = new Surface(surfaceTexture);
try {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setLooping(isLooping);
mMediaPlayer.setDataSource(getContext(), mSource);
mMediaPlayer.setSurface(surface);
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
mMediaPlayer.reset();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
surface.release();
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
}
This worked for me:
videoView.setBackgroundColor(Color.WHITE); // Your color.
videoView.start();
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
videoView.setBackgroundColor(Color.TRANSPARENT);
}
});
At least two years later, but I hope that was helpful.
This is definitely hacky, but better than overlaying an image (IMO).
boolean mRestored = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRestored = savedInstanceState != null;
}
#Override
public void onPrepared(MediaPlayer mp) {
if (!mRestored) vSurface.seekTo(1);
}
Assuming you are putting things into savedInstanceState in onSaveInstanceState.
Just show a frame from the video as preview.
vSurface.SeekTo(100);
Just use VideoView#setBackgroundDrawable(), I think.
initial settings.
VideoView.setBackgroundDrawable(yourdrawableid);
start video
VideoView.start();
VideoView.setBackgroundDrawable(0);
For people still looking for answer for this, calling VideoView.start() and VideoView.pause() in succession inside onPrepared worked for me. I know this may not be the ideal way of achieving this however it might be the one with minimal workaround required in the code. Hope this works for you too.
#Override
public void onPrepared(MediaPlayer mp) {
mVideoView.start();
mVideoView.pause();
}
This one works for me :
In XML : VideoView hide behind a Relative layout with white Background
<VideoView
android:id="#+id/myVideo"
android:layout_below="#+id/logo_top"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
/>
<RelativeLayout
android:id="#+id/mask"
android:background="#FFFFFF"
android:layout_below="#+id/logo_top"
android:layout_centerHorizontal="true"
android:layout_width="200dp" android:layout_height="200dp"
>
</RelativeLayout>
and in Activity : onCreate
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.acceuil);
myVideo = (VideoView) findViewById(R.id.myVideo);
mask = (RelativeLayout) findViewById(R.id.mask);
String path = "android.resource://"
+ getPackageName() + "/" + R.raw.anim_normal;
myVideo.setVideoURI(Uri.parse(path));
myVideo.start();
}
onStart :
public void onStart() {
final long time = System.currentTimeMillis();
super.onStart();
new CountDownTimer(5000, 100) {
#Override
public void onTick(long l) {
long time2 = System.currentTimeMillis();
if((time2 - time) > 500) {
mask.setVisibility(View.GONE);
}
}
}.start();
Hope this helps.
For me setting the setZOrderOnTop did not completely remove the initial black frame while playing an mp4 video. It, however, did reduce the time for which the black frame appears. I wanted to remove the initial black frame completely, so I played around and found that seeking the video forward by 100ms did the trick for me.
As a note, I am using the video in a loop, so if you do not want to loop the video just remove
mp.isLooping = true
Following is the snippet which I used to fix the issue:
val path = "android.resource://" + packageName + "/" + R.raw.my_video
videoView.setVideoURI(Uri.parse(path))
videoView.setZOrderOnTop(true)
videoView.seekTo(100)
videoView.start()
videoView.setOnPreparedListener { mp ->
videoView.setZOrderOnTop(false)
mp.isLooping = true // Loops the video
}
It would still be great if I get an exact explanation of why the above worked if someone finds it helpful.
Use svVideoView.seekTo(position).
Give Position within 5 (ms).
onPause():
position=svVideoView.getCurrentPosition()
onResume():
svVideoView.seekTo(position);
It works for me on both Activity and Fragment.
VideoView mVideo = (VideoView) findViewById(R.id.yourViewViewId);
mVideo.setVideoURI(mUri);
mVideo.setZOrderOnTop(false);
SurfaceHolder surfaceholder = mVideo.getHolder();
surfaceholder.setFormat(PixelFormat.TRANSPARENT);
It's a little late for this answer, but maybe other users have the same problem and find this question..
I have dealt with it, by setting a BackgroundResource initially and then, when starting the video, i have set the background to an invisible color..
VideoView myView = findViewById(R.id.my_view);
myView.setBackgroundResource(R.drawable.some_resource);
// some stuff
// this is when starting the video
myView.setVideoUri(someUri);
// also set MediaController somewhere...
//...
// now set the backgroundcolor to be not visible (first val of Color.argb(..) is the alpha)
myView.setBackGroundColor(Color.argb(0, 0, 0, 0));
//...
myView.start();
This is a nice solution:
package com.example.videoviewpractice;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.MediaController;
import android.widget.VideoView;
public class MainActivity extends Activity {
VideoView myVideoView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initVideo();
}
private void initVideo() {
myVideoView = (VideoView) findViewById(R.id.videoView1);
String url = "http://mtc.cdn.vine.co/r/videos/3DF00EB7001110633055418310656_1e50d6d9a65.3.2.mp4?" +
"versionId=KVMUFFGqe6rYRrGKgl8hxL6eakVAErPy";
myVideoView.setVideoURI(Uri.parse(url));
myVideoView.setMediaController(new MediaController(this));
myVideoView.requestFocus();
}
public void gone(View v){
myVideoView.setZOrderOnTop(true);
View placeholder = (View) findViewById(R.id.placeholder);
placeholder.setVisibility(View.GONE);
myVideoView.start();
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="${relativePackage}.${activityClass}" >
<FrameLayout
android:id="#+id/frameLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:layout_marginTop="50dip" >
<VideoView
android:id="#+id/videoView1"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="top|center"
android:visibility="visible" />
<FrameLayout
android:id="#+id/placeholder"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="top|center"
android:background="#drawable/ic_launcher"
android:onClick="gone" >
</FrameLayout>
</FrameLayout>
</LinearLayout>
To avoid annoying flickering and black screen issues I wrote FrameVideoView.
It takes benefits from 'placeholder solution' and (if your device is running API level 14 or higher) from TextureView, which is much more efficient than VideoView.
I wrote article on our blog to cover what it actually does.
It's simple to use:
Add FrameVideoView to layout:
<mateuszklimek.framevideoview.FrameVideoView
android:id="#+id/frame_video_view"
android:layout_width="#dimen/video_width"
android:layout_height="#dimen/video_height"
/>
find its instance in Activity and call corresponding methods in onResume and onPause:
public class SampleActivity extends Activity {
private FrameVideoView videoView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple);
String uriString = "android.resource://" + getPackageName() + "/" + R.raw.movie;
videoView = (FrameVideoView) findViewById(R.id.frame_video_view);
videoView.setup(Uri.parse(uriString), Color.GREEN);
}
#Override
protected void onResume() {
super.onResume();
videoView.onResume();
}
#Override
protected void onPause() {
videoView.onPause();
super.onPause();
}
}
I had the same issue. I found that the main reason for that was the use of FrameLayout as the parent layout. Use RelativeLayout as the parent layout of the VideoView
Modifying #emmgfx's answer worked for me:
videoView.setBackgroundColor(Color.WHITE)
videoView.start()
Timer().schedule(100){
videoView?.setBackgroundColor(Color.TRANSPARENT)
}
Trick is to delay the video view untill video loads.
PS : It's kotlin.
I found a great solution to this problem. ( in Kotlin )
Create an imageview over top of your videoview.
Create a function with a handler, and check if ( videoview.duration > 0 )
if the duration is greater than zero, then set the imageview.visibility to INVISABLE, and immediately follow by handler.removeCallbacks(this)
Call the above function after you have called videoview.start
Code below:
fun showVideoView() {
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
try {
if (videoplayer_topthree.currentPosition > 0) {
videoview_topthreeloadingimage.visibility = View.INVISIBLE
videoview_topthreeprogressbar.visibility = View.VISIBLE
videoview_topthreefullname.visibility = View.VISIBLE
videoview_topthreeviews.visibility = View.VISIBLE
videoview_topthreedate.visibility = View.VISIBLE
videoview_topthreedescription.visibility = View.VISIBLE
videoview_topthreedimview.visibility = View.VISIBLE
handler.removeCallbacks(this)
}
handler.postDelayed(this, 250)
} catch (e: Exception) {
println("SHOW VIDEOVIEW CATCH WAS CAUGHT")
}
}
}, 0)
}
and this is where I call this function..
videoplayer_topthree.setOnPreparedListener {
prepareSizing(it)
initializeProgressBar()
showVideoView()
}
Trying to play a mostly white video on a mostly white layout shows these glitches in a very obvious and annoying way, particularly during Activity transitions. The only way I managed to completely get rid of the glitches was to mash together a few different answers from this thread and elprl's answer at https://stackoverflow.com/a/9089245/3997253.
Create a solid colour View that covers the VideoView
<View
android:id="#+id/coverView"
android:background="#color/white"
android:layout_width="match_parent"
android:layout_height="match_parent" />
In onCreate
...
coverView = findViewById(R.id.coverView)
videoView = findViewById(R.id.videoView)
videoView.setZOrderOnTop(false)
val surfaceHolder = videoView.holder
surfaceHolder.setFormat(PixelFormat.TRANSPARENT)
In onStart
...
videoView.setOnPreparedListener { mp ->
// Fade out cover View to show VideoView once rendering has started
mp.setOnInfoListener { _, what, _ ->
if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
coverView.animate().alpha(0F)
return#setOnInfoListener true
}
return#setOnInfoListener false
}
mp.isLooping = true
videoView.start()
videoView.requestFocus()
}
When done with the VideoView
// Fade in cover View to hide the VideoView
coverView.animate().alpha(1F)
Only this worked for me:
fun VideoView.startWithRawFile(rawFileResId: Int, delay: Long = 0, backgroundColorResId: Int = R.color.white) {
setBackgroundResource(backgroundColorResId)
setVideoURI(uriToRawFile(rawFileResId))
setOnPreparedListener {
postDelayed(delay) {
start()
// replace 200 with your value according to your video file content:
postDelayed(200) { setBackgroundResource(R.color.transparent) }
}
}
}
// additional:
fun uriToRawFile(rawFileResId: Int) = Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE).authority(app().packageName).appendPath("$rawFileResId").build()
fun postDelayed(ms: Long, runnable: () -> Unit) = if (ms > 0) Handler(Looper.getMainLooper()).postDelayed(runnable, ms)
else runnable.invoke()
see this
VideoView videoView = (VideoView) findViewById(R.id.VideoView);
MediaController mediaController = new MediaController(this);
mediaController.setAnchorView(videoView);
Uri video = Uri.parse("android.resource://your_package_name/"+R.raw.monkeysonthebed_video);
videoView.setMediaController(mediaController);
videoView.setVideoURI(video);
videoView.start();

No video display when playing an mp4 file on Android

I try to render an mp4 file I added to my android ressources in res/raw like so:
public class Main extends RoboActivity
{
#InjectView(R.id.introVideo)
private VideoView introVideo;
private MediaPlayer player;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
player = MediaPlayer.create(this, R.raw.intro_video2);
SurfaceHolder holder = introVideo.getHolder();
player.setDisplay(holder);
player.start();
player.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp)
{
startActivity(new Intent(Main.this, Story.class));
releasePlayer();
}
});
}
#Override
protected void onPause()
{
super.onPause();
releasePlayer();
}
#Override
protected void onDestroy()
{
super.onDestroy();
releasePlayer();
}
private void releasePlayer()
{
if (player != null)
{
player.release();
}
}
}
but all I experience is the sound of the video, the screen keeps blank on my Samsung GalaxyTab. The source file is an mp4 file (H.264 AVC, 960x640, 30fps) and can be played perfectly fine with Quicktime and VLC.
I tried to downscale and resize the original video with Handbrake, down to 480x320 and 25fps, I tried several settings in handbrake, everything without success.
Is there anything obviously wrong with my code or is it the video format or something else - what am I doing wrong?
Thanks in advance,
Thomas.
I couldn't get the above way to work, but I found that it worked when I solely used the provided functionality of Android's VideoView like this:
...
String videoUri = "android.resource://my.package.path/raw/intro_video";
introVideo.setVideoURI(Uri.parse(videoUri));
introVideo.start();
...
Is it the 10.1v tab (running 3.0 or 3.1) or an "old" one running 2.2?
According to this: http://developer.android.com/guide/appendix/media-formats.html you seem to need 3.0+ for H.264 AVC.
I have had troubles with H 264 myself even with recommended values, had to downscale the sound even more. Might be worth looking in to.
Give MediaPlayer a PreparedListener. And call MediaPlayer.start() in PreparedListener.

Categories

Resources