This article starts with video playback, which is the most important part of the whole project, so try to say the details. Our video playback uses the Surfaceview+mediaplayer, the following step by step to see the specific implementation, first look:
One. Initialization
1. After entering playactivity, it is necessary to first initialize all the controls on this page, which is not much to say. Then look at the other initialized information:
@Override protected void Initview () {Mholder = MSv. Getholder();Mholder. SetType(Surfaceholder. Surface_type_push_buffers);Mholder. Addcallback(this);Intent Intent = Getintent ();Mvideofrom = Intent. Getintextra(contants. VIDEO_from, Contants. LOCAL);Mcurrentposition = Intent. Getintextra(contants. VIDEO_position,0);Mvideolist = (list<videoinfo>) Intent. Getserializableextra(contants. VIDEO_files);if (mvideolist = = NULL | | mvideolist. Size() ==0) {Toast. Maketext(This,"No video to play", Toast. LENGTH_short). Show();Finish ();} if (Mcurrentposition < mvideolist. Size()) {Mvideo = Mvideolist. Get(mcurrentposition);} visiblesurfacetopandbottom ();Mhandler. Sendemptymessage(system_time_chaned);}
First get the Surfaceholder object Mholder from Surfaceview, and then call the Addcallback method to set the callback interface for Mholder, which includes surfacecreated,surfacechanged, Surfacedestroyed three ways to control the life cycle of your surface inside the surfaceview. Then get the data passed by intent, assign to Mvideofrom (video source), Mcurrentposition (the location of the video in the collection), Mvideolist (Video collection), the collection to do some illegal judgment of the processing. Finally, the Visiblesurfacetopandbottom method is called and a system_time_chaned empty message is sent using Mhandler.
The Visiblesurfacetopandbottom method is detailed in the back, where we call to hide the top and bottom two layouts of the playback interface just for the first time. Look at the horizontal screen, you can see the upper right corner has a system time display, send system_time_chaned message is to get the system time and display in the upper right corner.
Of course, in the Setlistener also need to set some controls to listen, this will not stick code, back to the source of their own look.
Two. Playback/pause of video:
To play a video using Surfaceview, you must wait until its internal surface is initialized to finish, so initialize the MediaPlayer in the Surfacecreated method:
publicvoidsurfaceCreated(SurfaceHolder holder) { new MediaPlayer(); mVideoPlayer.setDisplay(mHolder); mVideoPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mVideoPlayer.setOnCompletionListener(PlayActivity.this); // 错误监听回调函数 mVideoPlayer.setOnErrorListener(PlayActivity.this); // 设置缓存变化监听 mVideoPlayer.setOnBufferingUpdateListener(PlayActivity.this); play(mPlayPosition); }
First create the MediaPlayer object Mvideoplayer, set the display to Mholder, then set the audio stream to Stream_music type, and then set a variety of monitoring for mvideoplayer. Finally the play method is called to play the video.
Private void Play(Final intPlayposition) {Try{//Get audio Focus if(Utils.getaudiofocus (playactivity. This,NULL)) {Mvideoplayer.reset (); Mvideoplayer.setdatasource (Mvideo.geturl ()); Mvideoplayer.prepare (); Mvideoplayer.setonpreparedlistener (NewMediaplayer.onpreparedlistener () {@Override Public void onprepared(MediaPlayer MP) {Try{Mvideoplayer.seekto (playposition); Mvideoplayer.setscreenonwhileplaying (true); Updateuiinfo (); Mvideoplayer.start (); }Catch(IllegalStateException e) {E.printstacktrace (); Toast.maketext (playactivity. This,"Illegal State", Toast.length_long). Show (); } } }); } }Catch(IOException e) {E.printstacktrace (); Toast.maketext ( This,"Load video error, possible format not supported!" ", Toast.length_long). Show (); }Catch(IllegalStateException e) {E.printstacktrace (); Toast.maketext ( This,"Illegal State", Toast.length_long). Show (); } }
To see this code, the first call to the Reset method is to mvideoplayer into the idle state, and then call Setdatasource to set the path to play the video, successfully entering the initialized state, If the Setdatasource method is called without an idle state, the illegalstateexception exception is thrown. In the initialized state, the prepare method is called to enter the prepared state, and the Onprepared method is called after success. If it is not a initialized state, calling the prepare method also throws a IllegalStateException exception.
In the Onprepared method, the video is now restored to the previously played position (if it was previously saved), the screen is always lit when playing, update the video related information on the interface, and finally, call the Start method to play the video, Mvideoplayer is in started state.
If the video format played is not supported, the IOException exception is thrown.
When the play button is clicked, if the video is playing, the playback is paused, if the video is paused, the video is played, and the code in the OnClick method is as follows:
case R.id.play_play: if (isPlaying()) { mVideoPlayer.pause(); changeState(PAUSE); else { if (mVideoState == PAUSE) { mVideoPlayer.start(); changeState(PLAY); elseif (mVideoState == STOP) { play(0); } } break;
Each time the playback pauses, you need to change the playback state through the Changestate method.
When the finish page or activity enters the stop state, the Surfacedestroyed method is called, and the resource that should be freed at this time is mvideoplayer because the Mvideoplayer is reinitialized the next time it comes in.
@Override publicvoidsurfaceDestroyed(SurfaceHolder holder) { ifnull) { changeState(STOP); mVideoPlayer.release(); null; } }
Three. Previous first, next function:
(1) Next song:
privatevoidplayNext() { mCurrentPosition++; if (mCurrentPosition < mVideoList.size()) { mVideo = mVideoList.get(mCurrentPosition); play(0); else { mCurrentPosition--; Toast.makeText(PlayActivity.this"已经是最后一个了", Toast.LENGTH_SHORT).show(); } }
Will mcurrentposition++, judge whether to go beyond the collection range, if not, get the current video, call play play this video. If it is already the last one, give a hint.
(2) Previous song:
privatevoidplayPrevious() { mCurrentPosition--; if0) { mVideo = mVideoList.get(mCurrentPosition); play(0); else { mCurrentPosition++; Toast.makeText(PlayActivity.this"已经是第一个了", Toast.LENGTH_SHORT).show(); } }
Will mcurrentposition–, determine whether it is less than 0, if not, then get the current video, call play play, if it is already the first one, then give a hint.
Four. Update the progress bar and fast forward rewind:
Progress bar We use the Seekbar control.
(1) Update the progress bar:
For video, the progress bar needs to start updating when the Start method is called, so we start updating the progress bar in the Changestate method when the state changes to play.
/** * Change the status of video * * @param State * * Private void changestate(intState) {mvideostate = state; Mhandler.sendemptymessage (state_changed);if(state = = PLAY) {Mhandler.post (NewRunnable () {@Override Public void Run() {/** prevents the OnDestroy method from being called, Mvideoplayer * is already null, but the message is still being sent here, resulting in a null pointer exception **/ if(Mvideoplayer = =NULL)return;intPosition = Mvideoplayer.getcurrentposition (); Mtopseekbar.setprogress (position); Mbottomseekbar.setprogress (position); Mtvplayedtime.settext (utils.formattostring (position));if(IsPlaying ()) {mhandler.postdelayed ( This, +); } } }); } }
As you can see, when state==play, we handler the Post method to start a thread update progress bar, updated every 1 seconds. There is a mvideoplayer in this operation, it is important to prevent the page exit when the call OnDestroy method release Mvideoplayer, handler this side still continue to send messages, at this time Mvideoplayer is already null, Call the Mvideoplayer.getcurrentposition () method to return a null pointer exception.
Here we have two progress bar, one in the vertical screen display, one in the horizontal screen display, vertical screen is not fast forward and rewind.
(2) Fast Forward Rewind:
Seekbar itself supports fast forward rewind, but after manual operation, we need to let mvideoplayer play in the corresponding position, so seekbar should register Onseekbarchangelistener Monitor:
Mtopseekbar.setonseekbarchangelistener (NewSeekbar.onseekbarchangelistener () {@Override Public void onprogresschanged(SeekBar SeekBar,intProgressBooleanFromuser) {}@Override Public void Onstarttrackingtouch(SeekBar SeekBar) { }@Override Public void Onstoptrackingtouch(SeekBar SeekBar) {mhandler.sendemptymessage (seekbar_touched); } }); }
As you can see, three methods are implemented in the listener, onprogresschanged is called when the progress bar changes, the Onstarttrackingtouch method is called when the progress bar is started, and Onstoptrackingtouch is called after the swipe is finished. Here we only need to send the seekbar_touched message after the swipe is over, and change the playback position of the Mvideoplayer.
The processing of this message in the Hanldermessage method:
case SEEKBAR_TOUCHED: mVideoPlayer.seekTo(mTopSeekBar.getProgress()); mBottomSeekBar.setProgress(mTopSeekBar.getProgress()); mTvPlayedTime.setText(Utils.formatToString(mTopSeekBar.getProgress())); break;
Call the Seekto method to change the playback position and update the played time.
(3) Update of online video cache:
Remember a bunch of snooping that was set up when initializing Mvideoplayer? There is a Mvideoplayer.setonbufferingupdatelistener (Playactivity.this), which is the listener that sets the cache change. When the cache changes, the Onbufferingupdate method is called, as follows:
/** * * @param MP * @param percent indicates the cache load progress, 0 is not started, 100 means the load is complete, * the load is complete to This method is always called after the @Override Public void onbufferingupdate(MediaPlayer MP,intpercent) {//If it is a local video, the cache progress is not updated (supposedly, the local cache does not call this method, //But do not know why RMVB format video will call this, so need to make this judgment) if(Mvideofrom = = contants.local)return;if(!isvideocachecomplate) {intSecond = (Mtopseekbar.getmax () * percent/ -); Mtopseekbar.setsecondaryprogress (second); Mbottomseekbar.setsecondaryprogress (second);if(Percent = = -) {isvideocachecomplate =true; } } }
The parameter precent represents the cache load progress, because when precent==100, this method is still called repeatedly, so a flag is set isvideocachecomplate when percent== When you leave isvideocachecomplate = True at 100, you do not need to set the cache progress again. Of course, when isvideocachecomplate = = False, the current cache progress needs to be calculated based on the value of percent and then set to Seekbar by the Setsecondaryprogress method.
According to personal understanding, onbufferingupdate only play online video will be called, but the test found in the playback of the local RMVB format is also called, so you need to determine the current video source Mvideofrom, if the local video returned directly.
Take a picture of the online video:
As you can see, the progress of the cache is yellow, compared to the horizontal screen before, no cache is grayed out.
This one will be a lot of things, there are two features playback interface top of the bottom of the layout of the hide and the screen of the shift in the next summary, this article is here.
Video Play (iii)--video playback