Android本地视频播放器mediaplay版

本文为自定义的视频播放器,可进行屏幕切换(由于换屏时大小变化,电脑截图就分开截图了),效果如下图:

--------------播放视频概括:

SurfaceView+MediaPlayer以及 VideoView 2种方式

SurfaceVIew中有个SurfaceHolder,通过surfaceView.getHolder( )方法获取,如果需要兼容2.3系统,还要再加上setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);否则只有声音没有图像。有3个生命周期 surfaceCreated、surfaceChanged、surfaceDestroyed。

读取raw目录下的2个方法

Uri.parse("android.resource://"+ getPackageName()+ "/" + R.raw.video));

AssetFileDescriptorafd = getResources().openRawResourceFd(

R.raw.por);

mp_test.setDataSource(afd.getFileDescriptor(),

afd.getStartOffset(), afd.getLength());

afd.close();

设置app主题无title

@android:style/Theme.Light.NoTitleBar

屏蔽变化引起的activity重启

android:configChanges="keyboard|orientation|screenSize"

设置屏幕常亮

getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

设置activity方向为纵向

android:screenOrientation="portrait"

获得当前屏幕是横屏还是竖屏

getResources().getConfiguration().orientation

手动切换横竖屏

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

监听横竖屏切换的动作

onConfigurationChanged

代码隐藏title

requestWindowFeature(Window.FEATURE_NO_TITLE);(必须在setcontent之前)

代码请求全屏模式

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

代码清除全屏模式

getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

适配不同的video尺寸达到不拉伸视频的效果

setVideoParms 方法,根据视频的比例和显示区域的比例计算得来

在一套布局里写2套控件,适配横竖屏

适当利用LinearLayout的weight属性做适配

使用audioManager获取,调节系统系统音量

seekBar的初始化,改变时的监听

系统音量改变时会发出broadcast

android.media.VOLUME_CHANGED_ACTION监听系统音量变化

seekBar监听时注意是否fromUser,mediaPlayer注意try

当前activity add的flag,当activity离开时就会clear掉

-------------项目代码VideoActivity类:

/**
 * 播放视频页面
 *
 * @author hasee
 *
 */
public class VideoActivity extends Activity implements OnPreparedListener,
		OnSeekBarChangeListener {
	private MediaPlayer mediaPlayer;
	private SurfaceView sv_video;
	private RelativeLayout rl_top;
	private Button bt_start_or_pause;// 播放或暂停按钮
	private SeekBar sb_progress;// 视频进度条
	ImageView mChangeSceen;
	ImageView mCenterPause;
	LinearLayout mllmenu;
	LinearLayout mlltitle;
	private SeekBar sb_vol;// 音量进度条
	private AudioManager am;
	private int currentPosition;// 记录当前进度值
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);// 双缓冲
		setContentView(R.layout.activity_video);

		initView();
		initData();

	}

	private void initView() {
		sv_video = (SurfaceView) findViewById(R.id.sv_video);
		bt_start_or_pause = (Button) findViewById(R.id.bt_start_or_pause);
		rl_top = (RelativeLayout) findViewById(R.id.rl_top);
		sb_progress = (SeekBar) findViewById(R.id.sb_progress);
		sb_vol = (SeekBar) findViewById(R.id.sb_vol);
		mChangeSceen= (ImageView) findViewById(R.id.bt_change);
		mCenterPause= (ImageView) findViewById(R.id.video_center_pause);
		mllmenu= (LinearLayout) findViewById(R.id.video_menu);
		mlltitle= (LinearLayout) findViewById(R.id.video_menu_title);
		// sv_video.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//
		// 兼容2.3及以下版本,否则只有声音没有画面
		sv_video.getHolder().addCallback(new Callback() {
			@Override
			public void surfaceCreated(SurfaceHolder holder) {
				// 当页面可见时候
				playVideo();
			}

			@Override
			public void surfaceDestroyed(SurfaceHolder holder) {
				// 当页面不可见时候
				stopVideo();
			}

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

			}
		});
	}

	private void initData() {
		registerReceiver(volReceiver, new IntentFilter(
				"android.media.VOLUME_CHANGED_ACTION"));
		am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
		// 保持屏幕常亮
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		sb_vol.setMax(am.getStreamMaxVolume(AudioManager.STREAM_MUSIC));
		sb_vol.setProgress(am.getStreamVolume(AudioManager.STREAM_MUSIC));
		sb_progress.setOnSeekBarChangeListener(this);
		sb_vol.setOnSeekBarChangeListener(this);
	}

	private void playVideo() {
		mediaPlayer = new MediaPlayer();
		try {
			// 获取RAW目录下的文件
			// AssetFileDescriptor afd = getResources().openRawResourceFd(
			// R.raw.por);
			// mediaPlayer.setDataSource(afd.getFileDescriptor(), 0,
			// afd.getLength());
			// 获取RAW目录下的文件
			mediaPlayer.setDataSource(
					this, Uri.parse("android.resource://" + getPackageName() + "/"+ R.raw.test));
			mediaPlayer.setLooping(true);
			mediaPlayer.setOnPreparedListener(this);
			mediaPlayer.setDisplay(sv_video.getHolder());
			mediaPlayer.prepareAsync();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void stopVideo() {
		canProgress = false;
		if (mediaPlayer != null && mediaPlayer.isPlaying()) {
			try {
				currentPosition = mediaPlayer.getCurrentPosition();
				mediaPlayer.pause();
				mediaPlayer.stop();
				mediaPlayer.release();
				mediaPlayer = null;
			} catch (Exception e) {
			}
		}
	}

	@Override
	public void onPrepared(MediaPlayer mp) {
		// 说明mediaPlayer准备好了
		try {
			sb_progress.setMax(mp.getDuration());
			setParam(mp, isLand());
			mp.start();
			if (currentPosition > 0) {
				mp.seekTo(currentPosition);
				currentPosition = 0;
			}
			startProgress();
		} catch (Exception e) {
		}
	}

	/**
	 * 判断当前是否横屏
	 *
	 * @return
	 */
	private boolean isLand() {
		if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
			return false;
		} else {
			return true;
		}
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
			Log.e("pid", "竖屏了");
			setParam(mediaPlayer, false);
			getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		} else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
			Log.e("pid", "横屏了");
			// 去除状态栏
			getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
			setParam(mediaPlayer, true);
		}
	}

	/**
	 * 设置视频尺寸,达到不被拉伸的效果
	 *
	 * @param mp
	 * @param isLand
	 */
	private void setParam(MediaPlayer mp, boolean isLand) {
		float screenWidth = getWindowManager().getDefaultDisplay().getWidth();
		float screenHeight = screenWidth / 16f * 9f;
		if (isLand) {
			screenHeight = getWindowManager().getDefaultDisplay().getHeight();
		}
		float videoWdith = mp.getVideoWidth();
		float videoHeight = mp.getVideoHeight();

		float screenPor = screenWidth / screenHeight;// 16:9
		float videoPor = videoWdith / videoHeight;// 9:16

		ViewGroup.LayoutParams pa = sv_video.getLayoutParams();
		if (videoPor <= screenPor) {
			pa.height = (int) screenHeight;
			pa.width = (int) (screenHeight * videoPor);
		} else {
			pa.width = (int) screenWidth;
			pa.height = (int) (screenWidth / videoPor);
		}
		ViewGroup.LayoutParams rl_pa = rl_top.getLayoutParams();
		rl_pa.width = pa.width;
		rl_pa.height = pa.height;
		rl_top.setLayoutParams(rl_pa);
		sv_video.setLayoutParams(pa);
	}

	private boolean canProgress = true;

	private void startProgress() {
		canProgress = true;
		new Thread() {
			public void run() {
				while (canProgress) {
					try {
						sleep(500);
						sb_progress.setProgress(mediaPlayer
								.getCurrentPosition());
					} catch (Exception e) {
					}
				}
			};
		}.start();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(volReceiver);
	}

	@Override
	public void onProgressChanged(SeekBar seekBar, int progress,
								  boolean fromUser) {
		if (!fromUser)
			return;
		switch (seekBar.getId()) {
			case R.id.sb_progress:
				try {
					if (mediaPlayer != null) {
						mediaPlayer.seekTo(progress);
					}
				} catch (Exception e) {
				}
				break;
			case R.id.sb_vol:
				am.setStreamVolume(AudioManager.STREAM_MUSIC, progress,
						AudioManager.FLAG_SHOW_UI);
				break;
		}
	}

	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {

	}

	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {

	}

	/**
	 * 系统音量改变时的广播接收者
	 */
	private BroadcastReceiver volReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			sb_vol.setProgress(am.getStreamVolume(AudioManager.STREAM_MUSIC));
		}
	};

	public void onClick(View v) {
		switch (v.getId()) {
			case R.id.bt_start_or_pause:
				try {
					if (bt_start_or_pause.getText().equals("播放")) {
						// 当前是播放,那么暂停
						mediaPlayer.pause();
						// canProgress = false;
						bt_start_or_pause.setText("暂停");
						mCenterPause.setVisibility(View.VISIBLE);
					} else if (bt_start_or_pause.getText().equals("暂停")) {
						// 当前是暂停,那么播放
						mediaPlayer.start();
						// canProgress = true;
						bt_start_or_pause.setText("播放");
						mCenterPause.setVisibility(View.GONE);
					}
				} catch (Exception e) {
				}
				break;
			case R.id.bt_change:
				// 点击了横竖屏切换,拿到当前屏幕方向
				if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
					// 当前是竖屏,切换成横屏
					setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
				} else {
					// 当前是横屏,切换成竖屏
					setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

				}
				break;
			case R.id.video_center_pause:

				try {
					if (bt_start_or_pause.getText().equals("播放")) {
						// 当前是播放,那么暂停
						mediaPlayer.pause();
						// canProgress = false;
						bt_start_or_pause.setText("暂停");
					} else if (bt_start_or_pause.getText().equals("暂停")) {
						// 当前是暂停,那么播放
						mediaPlayer.start();
						// canProgress = true;
						bt_start_or_pause.setText("播放");
					}
				} catch (Exception e) {
				}
				mCenterPause.setVisibility(View.GONE);
				break;
		}
	}
	/** 定时隐藏 */
	private Handler mDismissHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {

			mllmenu.setVisibility(View.GONE);
			mlltitle.setVisibility(View.GONE);  //标题栏的自动隐藏
		}
	};
	@Override
	public boolean onTouchEvent(MotionEvent event) {

		if (event.getAction()==MotionEvent.ACTION_DOWN){
			mllmenu.setVisibility(View.VISIBLE);
			mlltitle.setVisibility(View.VISIBLE);  //标题栏的显示
		}
		mDismissHandler.sendEmptyMessageDelayed(0,3000);
		return super.onTouchEvent(event);
	}
}

-----------------------activity_video.xml布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff"
    >

    <RelativeLayout
        android:id="@+id/rl_top"

        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000" >
        <LinearLayout
            android:id="@+id/video_menu_title"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:visibility="gone"
            android:background="#000">
            <TextView
                android:textColor="#fff"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="视频标题"/>
        </LinearLayout>
        <SurfaceView
            android:id="@+id/sv_video"
            android:layout_below="@+id/video_menu_title"
            android:layout_width="match_parent"
            android:layout_height="300dip"
            android:layout_centerInParent="true" />
        <ImageView
            android:id="@+id/video_center_pause"
            android:onClick="onClick"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerInParent="true"
            android:visibility="gone"
            android:src="@drawable/video_pause"/>
        <LinearLayout
            android:id="@+id/video_menu"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#000"
            android:visibility="gone"
            android:orientation="horizontal"
            android:layout_alignParentBottom="true">
            <Button
                android:id="@+id/bt_start_or_pause"
                android:layout_width="60dp"
                android:layout_height="50dp"
                android:text="播放"
                android:onClick="onClick"
                />
            <SeekBar
                android:id="@+id/sb_progress"
                android:layout_width="0dip"
                android:layout_height="50dip"
                android:layout_weight="3"
                android:indeterminate="false"/>

            <SeekBar
                android:id="@+id/sb_vol"
                android:layout_width="0dip"
                android:layout_height="50dip"
                android:layout_weight="1" />
            <ImageView
                android:id="@+id/bt_change"
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:onClick="onClick"
                android:src="@drawable/changescreen"/>

        </LinearLayout>

    </RelativeLayout>

</RelativeLayout>

注意在androidmanifest清单文件对activity中添加:

<span style="font-size:18px;">  android:configChanges="keyboard|orientation|screenSize"</span>

在res文件中建立raw文件夹,然后粘贴进去test.MP4文件播放.

由于谷歌已经停止对eclipse的版本更新,我使用Android studio软件开发,项目注意点图片:

Mainactivity类并没有什么代码,主要提供开发者一个自己项目页面,然后在里面用Intent跳转到VideoActivity.class就可以了,想要项目给我留言.

总结:

  1. 视频播放的注意点:

    1. 子线程里去更改视频进度条
    2. 注意try Catch对MediaPlayer进行操作的地方(包括自定义相机中的Camera也是)
    3. 记得SurFaceView的三个生命周期方法,每次最小化以后,回来再播放,都已经不是同一个Holder了
    4. 获取视频宽高比例和屏幕宽高比例进行比较,随后就可以根据屏幕的宽或者高来确定怎么拉伸视频不会导致变形
    5. MediaPlayer的释放:
      1. 先暂停
      2. 再停止
      3. 再释放
      4. 再置空
      5. 这样最安全
    1. 涉及到屏幕翻转的生命周期问题

      1. 要在AndroidManifest.xml中对视频的Activity做相应配置
    1. 使用广播来监听对应的音量变化,因为有时候我们是用音量键或者在设置里更改音量
    2. AssetFileDescriptor可以用来读取raw下文件的类

2,mediaPlayer的native机制:

a.   因为mediaPlayer中的很多方法如isPlaying()方法都是JNI机制写的,因此它的GC回收机制和平常的方法不一样。因此,很容易会出现非法堆栈异常等情况,因此最好加上一个tryCatch进行处理。

b.   内部都是native方法,这种native方法,在不一定的情况下,GC会进行回收,因此不知道什么时候调用就会跳出来一个IllegalStateException异常

时间: 2024-10-12 19:20:06

Android本地视频播放器mediaplay版的相关文章

Android本地文件点击视频播放器vitamio版

本博客用Android studio集成了vitamio的自定义视频播放器,同时可以在文件管理中选择播放,想要一个自己的视频播放器这边便可以满足 实现具体功能如下: 1.sd卡内存视频文件的点击播放; 2.视频时间进度条; 3.屏幕亮度的手势滑动; 4.视频音量的手势滑动; 5.多点手势效果 6.自定义存储视频url播放 想要的重要的代码,我懂得: ------------------------------软件工程结构图: 最重要的是依赖上vitamio库: -----------------

22_Android中的本地音乐播放器和网络音乐播放器的编写,本地视频播放器和网络视频播放器

?? 1 编写以下案例: 当点击了"播放"之后,在手机上的/mnt/sdcard2/natural.mp3就会播放. 2 编写布局文件activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_wi

最简单的基于DirectShow的示例:视频播放器自定义版

本文记录一个简单的基于DirectShow的自定义的视频播放器.这里所说的"自定义播放器",实际上指的是自己在Filter Graph中手动逐个添加Filter,并且连接这些Filter的后运行的播放器.这么做相对于使用RenderFile()这种"智能"创建Filter Graph的方法来说要复杂不少,但是可以让我们更加了解DirectShow的体系. 流程图 最简单的基于DirectShow的自定义的视频播放器的流程如下图所示. 该流程图中包含如下变量: IGr

Android自定义视频播放器(网络/本地)

最近因公司项目要求需要写一个播放器,自带的又不太好用,也不太好看.自能自定义啦.查看了很多资料,都没有完善的,还好得以为前辈的指点得以完成,感谢Yang.本篇里面我有可能有些地方写得不好(都附上了注释).希望各路大神指点,虚心受教. 先来个图(源码在后面附上) 视频列表里面的数据是本地的,如果你需要使用网络的只需要在此获取服务器的数据展示就行了.播放页面在后面. package com.eteng.moblieplayer.video; import android.content.Conten

Android进阶:自定义视频播放器开发(下)

上一篇文章我们主要讲了视频播放器开发之前需要准备的一个知识,TextureView,用于对图像流的处理.这篇文章开始构建一个基础的视频播放器. 一.准备工作 在之前的文章已经说过了,播放器也是一个view,我们要在这个view上播放视频流.所以我们要自定义一个简单的viewgroup,比如继承FrameLayout.还出就是布局简单,其他控件可以往上面添加.大家见过的视频播放器的控制器都是放在视频的上方的.这样就是用FrameLayout布局是最好的. class SmallVideoPlaye

Android进阶:十一、视频播放器初体验

上一篇文章我们主要讲了视频播放器开发之前需要准备的一直个知识,TextureView,用于对图像流的处理.这篇文章开始构建一个基础的视频播放器. 一.准备工作 在之前的文章已经说过了,播放器也是一个view,我们要在这个view上播放视频流.所以我们要自定义一个简单的viewgroup,比如继承FrameLayout.还出就是布局简单,其他控件可以往上面添加.大家见过的视频播放器的控制器都是放在视频的上方的.这样就是用FrameLayout布局是最好的. class SmallVideoPlay

有趣的安卓视频播放器(VideoView)

VideoView是安卓自带的视频播放器类,该类集成有显示和控制两大部分,在布局文件中添加VideoView然后在java文件中简单的调用控制命令,即可实现本地或者网络视频的播放.本章实现视频的居中播放.网络视频播放.本地视频播放.视频卡顿监听.网络连接错误监听.视频外自定义视频控件.视频内自定义视频控件等. 支持的格式:flv.3gp.mp4 类的一些重要方法 void start();                 //开始播放 void pause();              //暂

转:最简单的基于 DirectShow 的视频播放器

50行代码实现的一个最简单的基于 DirectShow 的视频播放器 本文介绍一个最简单的基于 DirectShow 的视频播放器.该播放器对于初学者来说是十分有用的,它包含了使用 DirectShow 播放视频所有必备的函数. 直接贴上代码,具体代码的含义都写在注释中了: /* 雷霄骅 * 中国传媒大学/数字电视技术 * [email protected] * */ // aviplayer.cpp : 定义控制台应用程序的入口点. #include "stdafx.h" #incl

轻松实现 网络视频播放器

最近用activeX media player做了个播放器,做过的人都知道,几乎没技术含量.在自己无聊的玩弄中,发现这个控件能够解析http.ftp协议,播放互联网的文件资源.现在被困在学校也无事可做,干脆把给这播放器写个服务器,用来播放互联网上的公开资源. media player的activeX控件十分强大,只需设置正确的url即可播放本地乃至互联网的文件资源.因此要给播放器做服务端,最重要的是向客户端传达正确的URL的信息.做服务器的思路便是:通过数据库记录下所有文件资源的信息与url,服