Android视频播放器---播放远程非流MP4

Android播放远程非流MP4文件


由于Android自带的Mediaplayer类,只能播放本地或者远程流形式的MP4文件,所以在播放远程非流的MP4,而且MP4的moov数据在文件的末尾时,在下载时,需要我们在写文件时做特殊处理,这样才能实现下载一部分,播放一部视频,下面来看实现代码:

下载代码部分
public class Mp4DownloadUtils {
	/** 播放MP4消息 */
	private static final int PLAYER_MP4_MSG = 0x1001;
	/** 下载MP4完成 */
	private static final int DOWNLOAD_MP4_COMPLETION = 0x1002;
	/** 下载MP4失败 */
	private static final int DOWNLOAD_MP4_FAIL = 0x1003;

	/**
	 * 下载MP4文件
	 * @param url
	 * @param fileName
	 * @param handler
	 * @return
	 */
	public static File downloadMp4File(final String url, final String fileName,
			final Handler handler) {
		final File mp4File = new File(fileName);
		downloadVideoToFile(url, mp4File, handler);
		return mp4File;
	}
	
	/**
	 * 下载视频数据到文件
	 * @param url
	 * @param dstFile
	 * @param moovSize
	 */
	private static void downloadVideoToFile(final String url, final File dstFile, final Handler handler) {
		Thread thread = new Thread() {
			
			@Override
			public void run() {
				super.run();
				try {
					URL request = new URL(url);
					HttpURLConnection httpConn = (HttpURLConnection) request.openConnection();
					httpConn.setConnectTimeout(3000);
					httpConn.setDoInput(true);
					httpConn.setDoOutput(true);
					httpConn.setDefaultUseCaches(false);
					
					httpConn.setRequestMethod("GET");
					httpConn.setRequestProperty("Charset", "UTF-8");
					httpConn.setRequestProperty("Accept-Encoding", "identity");
					
					int responseCode = httpConn.getResponseCode();
					if ((responseCode == HttpURLConnection.HTTP_OK)) {
						// 获取文件总长度
						int totalLength = httpConn.getContentLength();
						InputStream is = httpConn.getInputStream();
						
						if (dstFile.exists()) {
							dstFile.delete();
						}
						dstFile.createNewFile();
						RandomAccessFile raf = new RandomAccessFile(dstFile, "rw");
						BufferedInputStream bis = new BufferedInputStream(is);
						int readSize = 0;
						
						int mdatSize = 0;// mp4的mdat长度
						int headSize = 0;// mp4头长度
						byte[] boxSizeBuf = new byte[4];
						byte[] boxTypeBuf = new byte[4];
						// 由MP4的文件格式读取
						int boxSize = readBoxSize(bis, boxSizeBuf);
						String boxType = readBoxType(bis, boxTypeBuf);
						raf.write(boxSizeBuf);
						raf.write(boxTypeBuf);
						
						while (!boxType.equalsIgnoreCase("moov")) {
							int count = boxSize - 8;
							if (boxType.equalsIgnoreCase("ftyp")) {
								headSize += boxSize;
								byte[] ftyps = new byte[count];
								bis.read(ftyps, 0, count);
								raf.write(ftyps, 0, count);
							} else if (boxType.equalsIgnoreCase("mdat")) {
								// 标记mdat数据流位置,在后面reset时读取
								bis.mark(totalLength - headSize);
								// 跳过mdat数据
								skip(bis, count);
								mdatSize = count;
								byte[] mdatBuf = new byte[mdatSize];
								raf.write(mdatBuf);
							} else if (boxType.equalsIgnoreCase("free")) {
								headSize += boxSize;
							}
							
							boxSize = readBoxSize(bis, boxSizeBuf);
							boxType = readBoxType(bis, boxTypeBuf);
							raf.write(boxSizeBuf);
							raf.write(boxTypeBuf);
						}
						
						// 读取moov数据
						byte[] buffer = new byte[4096];
						int moovSize = 0;
						while ((readSize = bis.read(buffer)) != -1) {
							moovSize += readSize;
							raf.write(buffer, 0, readSize);
						}
						
						// 返回到mdat数据开始
						bis.reset();
						// 设置文件指针偏移到mdat位置
						long offset = raf.getFilePointer() - moovSize - mdatSize - 8;
						raf.seek(offset);
						
						// 读取mdat数据,设置mp4初始mdat的缓存大小
						int buf_size = 56 * 1024;// 56kb
						int downloadCount = 0;
						boolean viable = false;
						while (mdatSize > 0) {
							readSize = bis.read(buffer);
							raf.write(buffer, 0, readSize);
							mdatSize -= readSize;
							downloadCount += readSize;
							if (handler != null && !viable && downloadCount >= buf_size) {
								viable = true;
								// 发送开始播放视频消息
								sendMessage(handler, PLAYER_MP4_MSG, null);
							}
						}
						// 发送下载消息
						if (handler != null) {
							sendMessage(handler, DOWNLOAD_MP4_COMPLETION, null);
						}
						
						bis.close();
						is.close();
						raf.close();
						httpConn.disconnect();
					}
				} catch (Exception e) {
					e.printStackTrace();
					sendMessage(handler, DOWNLOAD_MP4_FAIL, null);
				}
			}
			
		};
		thread.start();
		thread = null;
	}
	
	/**
	 * 发送下载消息
	 * @param handler
	 * @param what
	 * @param obj
	 */
	private static void sendMessage(Handler handler, int what, Object obj) {
		if (handler != null) {
			Message msg = new Message();
			msg.what = what;
			msg.obj = obj;
			handler.sendMessage(msg);
		}
	}
	
	/**
	 * 跳转
	 * @param is
	 * @param count 跳转长度
	 * @throws IOException
	 */
	private static void skip(BufferedInputStream is, long count) throws IOException {
		while (count > 0) {
			long amt = is.skip(count);
			if (amt == -1) {
				throw new RuntimeException("inputStream skip exception");
			}
			count -= amt;
		}
	}
	
	/**
	 * 读取mp4文件box大小
	 * @param is
	 * @param buffer
	 * @return
	 */
	private  static int readBoxSize(InputStream is, byte[] buffer) {
		int sz = fill(is, buffer);
		if (sz == -1) {
			return 0;
		}
		
		return bytesToInt(buffer, 0, 4);
	}
	
	/**
	 * 读取MP4文件box类型
	 * @param is
	 * @param buffer
	 * @return
	 */
	private static String readBoxType(InputStream is, byte[] buffer) {
		fill(is, buffer);
		
		return byteToString(buffer);
	}
	
	/**
	 * byte转换int
	 * @param buffer
	 * @param pos
	 * @param bytes
	 * @return
	 */
	private static int bytesToInt(byte[] buffer, int pos, int bytes) {
		/*
		 * int intvalue = (buffer[pos + 0] & 0xFF) << 24 | (buffer[pos + 1] &
		 * 0xFF) << 16 | (buffer[pos + 2] & 0xFF) << 8 | buffer[pos + 3] & 0xFF;
		 */
		int retval = 0;
		for (int i = 0; i < bytes; ++i) {
			retval |= (buffer[pos + i] & 0xFF) << (8 * (bytes - i - 1));
		}
		return retval;
	}
	
	/**
	 * byte数据转换String
	 * @param buffer
	 * @return
	 */
	private static String byteToString(byte[] buffer) {
		assert buffer.length == 4;
		String retval = new String();
		try {
			retval = new String(buffer, 0, buffer.length, "ascii");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return retval;
	}
	
	private static int fill(InputStream stream, byte[] buffer) {
		return fill(stream, 0, buffer.length, buffer);
	}
	
	/**
	 * 读取流数据
	 * @param stream
	 * @param pos
	 * @param len
	 * @param buffer
	 * @return
	 */
	private static int fill(InputStream stream, int pos, int len, byte[] buffer) {
		int readSize = 0;
		try {
			readSize = stream.read(buffer, pos, len);
			if (readSize == -1) {
				return -1;
			}
			assert readSize == len : String.format("len %d readSize %d", len,
					readSize);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return readSize;
	}
}

播放器代码
public class VideoPlayerActivity extends Activity implements OnClickListener,
OnSeekBarChangeListener, OnPreparedListener, OnCompletionListener, OnTouchListener {

	private String url = null;// 播放地址
	private SurfaceView surfaceView = null;
	private SurfaceHolder surfaceHolder = null;
	private MediaPlayer mediaPlayer = null;
	private SeekBar seekBar = null;// 播放进度条
	private Button playBtn = null;// 播放
	private Button pauseBtn = null;// 暂停
	private TextView tv_current = null;
	private TextView tv_duration = null;
	private TextView divideTxt = null;
	private RelativeLayout controlLayout = null;// 操控界面布局
	private RelativeLayout mediaPlayerLayout = null;
	private LayoutParams mediaLayoutParams = null;
	private LinearLayout loadPrgLayout = null;

	private boolean isPlay = false;// 播放状态
	private boolean isPause = false;
	private boolean isShow = true;
	private boolean isEnd = false;
	private boolean isStartPlayer = false;
	private boolean isCustomStyle = false;// 开启自定义seekBar样式
	private int mDuration = 0;
	private int mSeekBarMax = 0;
	private int currentPosition = 0;// 当前播放位置
	private File mDownloadMP4File = null;
	private MediaPlayerCallback mCallback = null;

	private static final int playBtnID = 0x001;// 播放按钮id
	private static final int pauseBtnID = 0x002;// 暂停按钮id
	private static final int btnLayoutID = 0x003;// 按钮布局层id
	private static final int clockLayoutID = 0x004;// 计时布局层id
	private static final int surfaceViewID = 0x005;// surfaceView id
	/** 分秒 */
	private static final String DateFormatMS = "mm:ss";
	private static final int UPDATE_PROGRESS_MSG = 0x011;// 更新进度条和计时
	private static final int SHOW_CONTROL_MSG = 0x012;// 显示控制
	private static final int COMPLETION_PLAY_MSG = 0x013;// 播放完成

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN); // 全屏
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

		// 测试视频地址
		url = "http://minisite.adsame.com/nodie/v2.mp4";
		String videoFileName = getRootPath() + getVideo(url);
		
		mDownloadMP4File = Mp4DownloadUtils.downloadMp4File(url, videoFileName, videoHandler);
		initPlayer();
	}
	
	/**
	 * 获取外部存储根路径
	 * @return
	 */
	private String getRootPath() {
		String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath();
		return rootPath;
	}
	
	/**
	 * 获取视频文件名字
	 * @param videoUrl
	 * @return
	 */
	private String getVideo(String videoUrl) {
		int startIndex = videoUrl.lastIndexOf("/");
		return url.substring(startIndex, videoUrl.length());
	}
	
	/**
	 * 视频下载时的handler
	 */
	private Handler videoHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			switch (msg.what) {
			// 有MP4的mdat数据时,创建播放器
			case Mp4DownloadUtils.PLAYER_MP4_MSG:
				isStartPlayer = true;
				createMediaPlayer(mDownloadMP4File);
				break;
			case Mp4DownloadUtils.DOWNLOAD_MP4_COMPLETION:
				
				break;
			case Mp4DownloadUtils.DOWNLOAD_MP4_FAIL:
				
				break;

			default:
				break;
			}
		}
		
	};
	
	@SuppressWarnings("deprecation")
	private void initPlayer() {
		if (surfaceView == null) {
			surfaceView = new SurfaceView(this);
			surfaceView.getHolder()
				.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 不缓冲
			surfaceView.getHolder().setKeepScreenOn(true);// 保持屏幕高亮
			surfaceView.getHolder().addCallback(new SurfaceViewCallback());
			surfaceView.setId(surfaceViewID);
			surfaceView.setOnClickListener(this);
			surfaceView.setEnabled(false);
			surfaceHolder = surfaceView.getHolder();
		}

		initPlayerButton();
		RelativeLayout btnLayout = new RelativeLayout(this);
		btnLayout.setId(btnLayoutID);
		btnLayout.setGravity(Gravity.CENTER);
		LayoutParams btnParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT);
		btnParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
		btnParams.addRule(RelativeLayout.CENTER_VERTICAL);
		btnLayout.setLayoutParams(btnParams);
		btnLayout.addView(playBtn);
		btnLayout.addView(pauseBtn);

		initClockTextView();
		LinearLayout clockLayout = new LinearLayout(this);
		clockLayout.setId(clockLayoutID);
		clockLayout.setOrientation(LinearLayout.HORIZONTAL);
		LayoutParams clockTVLayoutParams = new LayoutParams(
				LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		clockTVLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
		clockTVLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
		clockLayout.setLayoutParams(clockTVLayoutParams);
		clockLayout.addView(tv_duration);
		clockLayout.addView(divideTxt);
		clockLayout.addView(tv_current);
		// 进度条必须在btnLayout和prgLayout后面
		initProgressSeekBar();
		// 下载进度
		initProgressBar();

		controlLayout = new RelativeLayout(this);
		LayoutParams rlParams = new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.WRAP_CONTENT);
		rlParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		int marginLeft = 5;
		int marginRight = 5;
		rlParams.setMargins(marginLeft, 0, marginRight, 0);
		controlLayout.setLayoutParams(rlParams);
		controlLayout.setVisibility(View.GONE);
		controlLayout.setBackgroundColor(Color.GRAY);
		controlLayout.addView(btnLayout);
		controlLayout.addView(seekBar);
		controlLayout.addView(clockLayout);
		
		setViewDrawableStyle();

		mediaPlayerLayout = new RelativeLayout(this);
		mediaLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
		mediaLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
		mediaPlayerLayout.setLayoutParams(mediaLayoutParams);
		mediaPlayerLayout.addView(surfaceView);
		mediaPlayerLayout.addView(controlLayout);
		mediaPlayerLayout.addView(loadPrgLayout);
		RelativeLayout layout = new RelativeLayout(this);
		layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 
				LayoutParams.MATCH_PARENT));
		layout.addView(mediaPlayerLayout);
		setContentView(layout);
	}

	private void initPlayerButton() {
		int btnWidth = 60;
		int btnHeight = 60;
		// 开始按钮
		playBtn = new Button(this);
		playBtn.setId(playBtnID);
		playBtn.setOnClickListener(this);
		LayoutParams playParams = new LayoutParams(btnWidth, btnHeight);
		playBtn.setLayoutParams(playParams);
		playBtn.setEnabled(false);

		// 暂停按钮
		pauseBtn = new Button(this);
		pauseBtn.setId(pauseBtnID);
		pauseBtn.setOnClickListener(this);
		LayoutParams pauseParams = new LayoutParams(btnWidth, btnHeight);
		pauseBtn.setLayoutParams(pauseParams);
		pauseBtn.setVisibility(View.GONE);
	}

	private void initClockTextView() {
		// 播放进度
		tv_current = new TextView(this);
		tv_current.setTextColor(Color.WHITE);
		tv_current.setText(getStringByFormat(currentPosition, DateFormatMS));
		// 播放持续时间
		tv_duration = new TextView(this);
		tv_duration.setTextColor(Color.WHITE);
		tv_duration.setText(getStringByFormat(mDuration, DateFormatMS));
		// 分隔线
		divideTxt = new TextView(this);
		divideTxt.setText("/");
		divideTxt.setTextColor(Color.WHITE);
	}

	private void initProgressSeekBar() {
		// 播放进度条
		seekBar = new SeekBar(this);
		seekBar.setOnSeekBarChangeListener(this);
		LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.WRAP_CONTENT);
		params.addRule(RelativeLayout.RIGHT_OF, btnLayoutID);
		params.addRule(RelativeLayout.LEFT_OF, clockLayoutID);
		params.addRule(RelativeLayout.CENTER_VERTICAL);
		int leftpadding = 15;
		int rightpadding = 15;
		seekBar.setLayoutParams(params);
		seekBar.setPadding(leftpadding, 0, rightpadding, 0);
		seekBar.setOnTouchListener(this);
	}
	
	private void initProgressBar() {
		LayoutParams layoutParams = new LayoutParams(
				LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
		
		loadPrgLayout = new LinearLayout(this);
		loadPrgLayout.setLayoutParams(layoutParams);
		loadPrgLayout.setOrientation(LinearLayout.VERTICAL);
		loadPrgLayout.setVisibility(View.VISIBLE);
		loadPrgLayout.setGravity(Gravity.CENTER);
		
		LayoutParams params = new LayoutParams(
				LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		ProgressBar loadPrgBar = new ProgressBar(this);
		loadPrgBar.setLayoutParams(params);
		loadPrgLayout.addView(loadPrgBar);
	}

	/**
	 * 设置view的样式
	 */
	private void setViewDrawableStyle() {
		try {
			// setBackground报错需要Android版本号在16以上
			playBtn.setBackground(createSelectorDrawable(
					getAssetDrawable(this, "btn_play_normal.png"),
					getAssetDrawable(this, "btn_play_pressed.png")));
			pauseBtn.setBackground(createSelectorDrawable(
					getAssetDrawable(this, "btn_pause_normal.png"),
					getAssetDrawable(this, "btn_pause_pressed.png")));
			if (isCustomStyle) {
				seekBar.setThumb(createSeekbarThumbDrawable(
						getAssetDrawable(this, "progress_thumb_normal.png"),
						getAssetDrawable(this, "progress_thumb_pressed.png")));
				seekBar.setProgressDrawable(createSeekbarProgressDrawable(
						getAssetDrawable(this, "progress_bg.png"),
						getAssetDrawable(this, "progress_secondary.png"), 
						getAssetDrawable(this, "progress.png")));
				controlLayout.setBackground(getAssetDrawable(this, "ctrl_layout_bg.png"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取accets目录下的图片
	 * @param context
	 * @param imageName
	 * @return
	 * @throws IOException
	 */
	public Drawable getAssetDrawable(Context context, String imageName) throws IOException {
		return BitmapDrawable.createFromStream(context.getAssets().open(imageName), imageName);
	}

	/**
	 * 创建图片选择器
	 * @param normal
	 * @param pressed
	 * @return
	 */
	private Drawable createSelectorDrawable(Drawable normal, Drawable pressed) {
		StateListDrawable drawable = new StateListDrawable();
		drawable.addState(new int[] { android.R.attr.state_focused },
				normal);
		drawable.addState(new int[] { android.R.attr.state_pressed },
				pressed);
		drawable.addState(new int[] {}, normal);
		return drawable;
	}

	/**
	 * 创建seekBar拖动点样式图
	 * 
	 * @param normal
	 *            正常
	 * @param pressed
	 *            下压
	 * @return
	 */
	private Drawable createSeekbarThumbDrawable(Drawable normal, Drawable pressed) {
		StateListDrawable drawable = new StateListDrawable();
		drawable.addState(new int[] { android.R.attr.state_pressed }, pressed);
		drawable.addState(new int[] { android.R.attr.state_focused }, normal);
		drawable.addState(new int[] {}, normal);
		return drawable;
	}

	/**
	 * 创建seekBar进度样式图
	 * 
	 * @param background
	 *            背景图
	 * @param secondaryProgress
	 *            第二进度能量图
	 * @param progress
	 *            进度能量图
	 * @return
	 */
	private Drawable createSeekbarProgressDrawable(Drawable background,
			Drawable secondaryProgress, Drawable progress) {
		LayerDrawable progressDrawable = (LayerDrawable) seekBar
				.getProgressDrawable();
		int itmeSize = progressDrawable.getNumberOfLayers();
		Drawable[] outDrawables = new Drawable[itmeSize];
		for (int i = 0; i < itmeSize; i++) {
			switch (progressDrawable.getId(i)) {
			case android.R.id.background:// 设置进度条背景
				outDrawables[i] = background;
				break;
			case android.R.id.secondaryProgress:// 设置二级进度条
				outDrawables[i] = secondaryProgress;
				break;
			case android.R.id.progress:// 设置进度条
				ClipDrawable oidDrawable = (ClipDrawable) progressDrawable
						.getDrawable(i);
				Drawable drawable = progress;
				ClipDrawable proDrawable = new ClipDrawable(drawable,
						Gravity.LEFT, ClipDrawable.HORIZONTAL);
				proDrawable.setLevel(oidDrawable.getLevel());
				outDrawables[i] = proDrawable;
				break;
			default:
				break;
			}
		}
		progressDrawable = new LayerDrawable(outDrawables);
		return progressDrawable;
	}
	
	private class SurfaceViewCallback implements
			android.view.SurfaceHolder.Callback {

		@Override
		public void surfaceCreated(SurfaceHolder holder) {
			createMediaPlayer();
		}

		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width,
				int height) {
		}

		@Override
		public void surfaceDestroyed(SurfaceHolder holder) {
			destroy();
		}
	}
	
	private void createMediaPlayer() {
		createMediaPlayer(null);
	}
	
	private void createMediaPlayer(File videoFile) {
		if (url == null && videoFile == null && !isStartPlayer) {
			return;
		}
		
		if (mediaPlayer == null) {
			mediaPlayer = new MediaPlayer();
		}
		mediaPlayer.reset();
		mediaPlayer.setOnCompletionListener(this);
		mediaPlayer.setOnPreparedListener(this);// 设置监听事件
		mediaPlayer.setDisplay(surfaceHolder);// 把视频显示到surfaceView上
		if (videoFile != null) {
			setDataSource(videoFile);
		} else {
			setDataSource(url);
		}
		mediaPlayer.prepareAsync();// 准备播放
	}
	
	private void setDataSource(String src) {
		try {
			if (src.startsWith("http://")) {
				mediaPlayer.setDataSource(this, Uri.parse(src));
			} else {
				setDataSource(new File(src));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void setDataSource(File file) {
		try {
			FileInputStream fis = new FileInputStream(file);
			mediaPlayer.setDataSource(fis.getFD());// 设置播放路径
			fis.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onPrepared(MediaPlayer mp) {
		if (isPause) {
			
		} else {
			mediaPlayer.start();
			playBtn.setEnabled(true);
			playBtn.setVisibility(View.GONE);
			pauseBtn.setVisibility(View.VISIBLE);
			
			if (currentPosition > 0) {
				mediaPlayer.seekTo(currentPosition);
			}
		}
		loadPrgLayout.setVisibility(View.GONE);
		controlLayout.setVisibility(View.VISIBLE);
		surfaceView.setEnabled(true);
		mHandler.post(showControlRunnable);
		mHandler.post(updateProgressRunnable);
		
		if (mSeekBarMax == 0 || mDuration == 0) {
			mSeekBarMax = seekBar.getMax();
			mDuration = mediaPlayer.getDuration();
			mediaPlayerLayout.setMinimumHeight(520);
			mediaPlayerLayout.invalidate();
		}
	}

	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {
		removeHandlerTask();
	}

	@Override
	public void onProgressChanged(SeekBar seekBar, int progress,
			boolean fromUser) {
		if (fromUser) {
			int milliseconds = countPosition(progress);
			tv_current.setText(getStringByFormat(milliseconds, DateFormatMS));
		}
	}

	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {
		if (mediaPlayer != null) {
			currentPosition = countPosition(seekBar.getProgress());
			mediaPlayer.seekTo(currentPosition);
			restartHandlerTack();
		}
	}
	
	private void start() {
		if (mediaPlayer != null && isPause) {
			isPlay = true;
			isPause = false;
			if (currentPosition > 0) {
				mediaPlayer.seekTo(currentPosition);
			}
			mediaPlayer.start();
			restartHandlerTack();
			playBtn.setVisibility(View.GONE);
			pauseBtn.setVisibility(View.VISIBLE);
		}
	}
	
	private void pause() {
		if (mediaPlayer != null && mediaPlayer.isPlaying()) {
			isPlay = false;
			isPause = true;
			mediaPlayer.pause();
			playBtn.setVisibility(View.VISIBLE);
			pauseBtn.setVisibility(View.GONE);
			controlLayout.setVisibility(View.VISIBLE);
			currentPosition = mediaPlayer.getCurrentPosition();
			removeHandlerTask();
		}
	}
	
	private void destroy() {
		if (mediaPlayer != null && isPlay) {
			mediaPlayer.pause();
			controlLayout.setVisibility(View.VISIBLE);
			currentPosition = mediaPlayer.getCurrentPosition();
			removeHandlerTask();
		}
	}
	
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case playBtnID:// 播放
			if (mediaPlayer == null) {
				createMediaPlayer();
			} else {
				start();
			}
			break;
		case pauseBtnID:// 暂停
			pause();
			break;
		case surfaceViewID:
			// 先移除任务,如果显示,重新post一次任务
			mHandler.removeCallbacks(showControlRunnable);
			if (isShow) {
				controlLayout.setVisibility(View.GONE);
				isShow = false;
			} else {
				controlLayout.setVisibility(View.VISIBLE);
				isShow = true;
				mHandler.postDelayed(showControlRunnable, 5000);
			}
			break;
		default:
			break;
		}
	}
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		if (v == seekBar && isEnd) {
			return true;
		}
		return false;
	}
	
	@Override
	public void onDetachedFromWindow() {
		super.onDetachedFromWindow();
	}

	@Override
	public void onCompletion(MediaPlayer mp) {
		// 完成播放
		try {
			isEnd = true;
			isPlay = false;
			seekBar.setProgress(countProgress(mDuration));
			tv_current.setText(getStringByFormat(mDuration, DateFormatMS));
			playBtn.setVisibility(View.VISIBLE);
			pauseBtn.setVisibility(View.GONE);
			controlLayout.setVisibility(View.VISIBLE);
			playBtn.setEnabled(false);
			removeHandlerTask();
			mHandler.sendEmptyMessageDelayed(COMPLETION_PLAY_MSG, 500);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public interface MediaPlayerCallback {
		public void onCompletion();
	}
	
	public void setMediaPlayerCallback(MediaPlayerCallback callback) {
		mCallback = callback;
	}

	private Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			switch (msg.what) {
			case UPDATE_PROGRESS_MSG:// 更新播放进度和计时器
				if (mediaPlayer != null && mediaPlayer.isPlaying()) {
					isPlay = true;
					currentPosition = mediaPlayer.getCurrentPosition();
					seekBar.setProgress(countProgress(currentPosition));
					tv_current
							.setText(getStringByFormat(currentPosition, DateFormatMS));
					tv_duration.setText(getStringByFormat(mDuration, DateFormatMS));
				}
				break;
			case SHOW_CONTROL_MSG:// 显示播放控制控件
				if (isPlay) {
					isShow = false;
					controlLayout.setVisibility(View.GONE);
					mHandler.removeCallbacks(showControlRunnable);
				} else {
					isShow = true;
					controlLayout.setVisibility(View.VISIBLE);
				}
				break;
			case COMPLETION_PLAY_MSG:// 完成播放
				finish();
				if (mCallback != null) {
					mCallback.onCompletion();
				}
				Settings.System.getInt(getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);
				break;

			default:
				break;
			}
		}

	};
	
	/**
	 * 重新启动任务
	 */
	private void restartHandlerTack() {
		mHandler.post(updateProgressRunnable);
		mHandler.postDelayed(showControlRunnable, 5000);
	}
	
	/**
	 * 移除任务
	 */
	private void removeHandlerTask() {
		mHandler.removeCallbacks(showControlRunnable);
		mHandler.removeCallbacks(updateProgressRunnable);
	}

	/**
	 * 更新进度条线程
	 */
	private Runnable updateProgressRunnable = new Runnable() {

		@Override
		public void run() {
			mHandler.sendEmptyMessage(UPDATE_PROGRESS_MSG);
			mHandler.postDelayed(updateProgressRunnable, 1000);
		}
	};

	/**
	 * 显示控制线程
	 */
	private Runnable showControlRunnable = new Runnable() {

		@Override
		public void run() {
			mHandler.sendEmptyMessage(SHOW_CONTROL_MSG);
			mHandler.postDelayed(showControlRunnable, 5000);
		}
	};

	/**
	 * 计算播放进度
	 * 
	 * @param position
	 * @return
	 */
	private int countProgress(int position) {
		if (mDuration == 0) {
			return 0;
		}

		return position * mSeekBarMax / mDuration;
	}

	/**
	 * 计算播放位置
	 * 
	 * @param progress
	 * @return
	 */
	private int countPosition(int progress) {
		if (mSeekBarMax == 0) {
			return 0;
		}

		return progress * mDuration / mSeekBarMax;
	}

	/**
	 * 描述:获取milliseconds表示的日期时间的字符串.
	 * 
	 * @param milliseconds
	 * @param format
	 *            格式化字符串,如:"yyyy-MM-dd HH:mm:ss"
	 * @return String 日期时间字符串
	 */
	private String getStringByFormat(long milliseconds, String format) {
		String thisDateTime = null;
		try {
			SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
			mSimpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+0"));
			thisDateTime = mSimpleDateFormat.format(milliseconds);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return thisDateTime;
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		
		if (mediaPlayer != null) {
			mediaPlayer.stop();
			mediaPlayer.release();
			mediaPlayer = null;
			currentPosition = 0;
		}
		System.gc();
	}
	
}

需要加的权限
<uses-permission android:name="android.permission.INTERNET" />




  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值