SurfaceView获取本地视频播放
1、定义
可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图容器。
它的特性是:可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。
2、实现
首先继承SurfaceView并实现SurfaceHolder.Callback接口
使用接口的原因:因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始(Surface—表面,这个概念在 图形编程中常常被提到。基本上我们可以把它当作显存的一个映射,写入到Surface 的内容
可以被直接复制到显存从而显示出来,这使得显示速度会非常快),而在Surface 被销毁之前必须结束。所以Callback
中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。
需要重写的方法
(1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}
//在surface的大小发生改变时激发
(2)public void surfaceCreated(SurfaceHolder holder){}
//在创建时激发,一般在这里调用画图的线程。
(3)public void surfaceDestroyed(SurfaceHolder holder) {}
//销毁时激发,一般在这里将画图的线程停止、释放。
整个过程:继承SurfaceView并实现SurfaceHolder.Callback接口
----> SurfaceView.getHolder()获得SurfaceHolder对象
---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布---->
Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas
canvas)结束锁定画图,并提交改变,将图形显示。
3、SurfaceHolder
这里用到了一个类SurfaceHolder,可以把它当成surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。
几个需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。
4、实例:
主布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.bwie.surfaceview.MainActivity"> <Button
android:id="@+id/buffer_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SurfaceVie使用缓冲刷新UI几面"/> <Button
android:id="@+id/video_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SurfaceView播放视频"/> </LinearLayout>
主布局的Activity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
protected Button mBufferBtn;
protected Button mVideoBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
initView();
}
@Override
public void onClick(View view) {
Intent intent = new Intent();
if (view.getId() == R.id.buffer_btn) {
intent.setClass(this, BufferActivity.class);
} else if (view.getId() == R.id.video_btn) {
intent.setClass(this, VideoActivity.class);
}
startActivity(intent);
}
private void initView() {
mBufferBtn = (Button) findViewById(R.id.buffer_btn);
mBufferBtn.setOnClickListener(MainActivity.this);
mVideoBtn = (Button) findViewById(R.id.video_btn);
mVideoBtn.setOnClickListener(MainActivity.this);
}
}
SurfaceVie使用缓冲刷新UI几面:
布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.bwie.surfaceview.activity.BufferActivity"> <SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </RelativeLayout>
Activity
// SurfaceView配合子线程,使用双缓冲刷新UI界面
// SurfaceHolder:用于管理缓冲区Surface的类(生命周期)
public class BufferActivity extends AppCompatActivity implements SurfaceHolder.Callback { protected SurfaceView mSurfaceView;
private SurfaceHolder mHolder; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_buffer);
initView();
initSurfaceHolder();
} // 初始化Surface的管理者
private void initSurfaceHolder() {
mHolder = mSurfaceView.getHolder();
// 添加管理生命周期的接口回调
mHolder.addCallback(this);
} private void initView() {
mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
} // 缓冲区创建
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d("1507", "surfaceCreated");
new DrawThread(this).start();
} // 缓冲区内容改变(子线程渲染UI的过程)
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d("1507", "surfaceChanged");
} // 缓冲区销毁
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d("1507", "surfaceDestroyed");
} // 绘制UI的子线程
private static class DrawThread extends Thread { // private BufferActivity mActivity; // 使用弱引用持有Activity的实例,
// 当Activity销毁时,当前线程会释放Activity,避免Activity无法释放导致的内存泄漏
private WeakReference<BufferActivity> mWeakReference; public DrawThread(BufferActivity activity) {
// mActivity = activity;
mWeakReference = new WeakReference<BufferActivity>(activity);
} @Override
public void run() {
super.run(); // 通过弱引用获取持有的Activity实例
BufferActivity activity = mWeakReference.get(); if (activity == null) {
return;
} // 获取SurfaceView的盖度
int height = activity.mSurfaceView.getHeight(); // 创建画笔
Paint paint = new Paint();
paint.setColor(Color.GREEN);// 画笔颜色
paint.setStrokeWidth(10);// 画笔粗细。注意:Java中设置的尺寸单位都是px
paint.setStyle(Paint.Style.FILL_AND_STROKE);// 设置实心
paint.setAntiAlias(true);// 设置是否抗锯齿 Canvas canvas = null;
for (int i = 0; i < height; i += 5) { // 获取Surface中的画布
canvas = activity.mHolder.lockCanvas();// 锁定画布 // 当SurfaceView随着Activity销毁时,缓冲区Surface也会随着销毁,无法获取缓冲区的画布
if (canvas == null) {
return;
} canvas.drawColor(Color.RED);// 设置画布背景覆盖之前的残影
// 使用画笔在画布上绘制指定形状
canvas.drawCircle(100, i + 50, 50, paint);// 圆心x坐标,圆心y坐标,半径,画笔 // 缓冲区的画布绘制完毕,需要解锁并提交给窗口展示
activity.mHolder.unlockCanvasAndPost(canvas); } }
} }
SurfaceView播放视频:
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.bwie.surfaceview.activity.VideoActivity"> <Button
android:id="@+id/play_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"/> <net.bwie.surfaceview.widget.MyVideoSurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </LinearLayout>
Activity
/**
* 1、获取播放源
* 2、准备SurfaceView
* 3、多媒体交给MediaPlayer处理
*/
public class VideoActivity extends AppCompatActivity implements View.OnClickListener { protected MyVideoSurfaceView mSurfaceView;
protected Button mPlayBtn; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_video);
initView(); } // 运行、可见
@Override
protected void onStart() {
super.onStart();
} // 可交互
@Override
protected void onResume() {
super.onResume();
} private void play() {
String videoPath = Environment.getExternalStorageDirectory().getPath() +
"/VID_20171117_144736.3gp";// 外部存储根路径 mSurfaceView.playVideo(videoPath);
} private void initView() {
mSurfaceView = (MyVideoSurfaceView) findViewById(R.id.surface_view);
mPlayBtn = (Button) findViewById(R.id.play_btn);
mPlayBtn.setOnClickListener(VideoActivity.this);
} @Override
public void onClick(View view) {
if (view.getId() == R.id.play_btn) {
play();
}
}
}
MyVideoSurfaceView类:
public class MyVideoSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private MediaPlayer mMediaPlayer;
public MyVideoSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// 获取Surface换朝哪个区的持有者
mHolder = getHolder();
mHolder.addCallback(this);
}
// 设置播放源
public void playVideo(String path) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
}
try {
// 设置播放源
mMediaPlayer.setDataSource(path);
// 设置多媒体的显示部分:使用SurfaceHolder渲染画面
mMediaPlayer.setDisplay(mHolder);
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mMediaPlayer.release();
mMediaPlayer = null;
}
}
别忘了加权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
SurfaceView获取本地视频播放的更多相关文章
- VideoView获取本地视频播放
主布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...
- Win10《芒果TV》更新v3.5.0夏至版:会员尊享蓝光画质,关联本地视频播放
在Win10秋季创意者更新前夕,Win10版<芒果TV>全平台同步更新夏至版v3.5.0,新增会员蓝光画质,关联本地视频播放,进一步提升使用体验. Win10版<芒果TV>V3 ...
- android获取本地图片并显示图片
import java.io.FileNotFoundException; import android.content.ContentResolver; import android.content ...
- Linux编程获取本地IP
#include <stdio.h> #include <sys/types.h> #include <ifaddrs.h> #include <netine ...
- Java获取本地IP地址
import java.net.InetAddress; import java.net.UnknownHostException; public class IpTest { public stat ...
- Android开发之获取本地视频和获取自拍视频
1.获取本地所有视频 public void getLoadMedia() { Cursor cursor = UILApplication.instance.getApplicationContex ...
- 获取本地IP地址信息
2012-06-05 /// <summary> /// 获取本地IP地址信息 /// </summary> void G ...
- 用angular实时获取本地localStorage数据,实现一个模拟后台数据登入的效果
研究了一上午,终于做出了,实时获取本地localStorage来模拟注册登入~~~ <!DOCTYPE html><html><head lang="en&qu ...
- Android之获取本地图片并压缩方法
这两天在做项目时,做到上传图片功能一块时,碰到两个问题,一个是如何获取所选图片的路径,一个是如何压缩图片,在查了一些资料和看了别人写的后总算折腾出来了,在此记录一下. 首先既然要选择图片,我们就先要获 ...
随机推荐
- Oracle RAC 环境下的连接管理(转) --- 防止原文连接失效
崔华老师的文章!!! 这篇文章详细介绍了Oracle RAC环境下的连接管理,分别介绍了什么是 Connect Time Load Balancing.Runtime Connection Load ...
- 前端基础——css
前端基础——css css的内容主要包括:盒子模型.定位.单位与取值.属性.选择器.
- C#:时间日期操作(持续更新)
1.给定时间戳返回指定的时间格式 private string StampToDate(string timeStamp,string format) { DateTime dtStart = Tim ...
- ActiveMQ新的Master/Slave存储共享机制Replicated LevelDB Store
ActiveMQ新的Master/Slave存储共享机制Replicated LevelDB Store 使用ZooKeeper协调选择一个node作为master.被选择的master broker ...
- spring 资源访问
spring 资源访问 Resource resource=null; //访问网络资源 resource=new UrlResource("file:bool.xml"); // ...
- Enterprise Library 6 学习笔记
今天是2014年上班第一天,想着今年要做点与往年不同的事情,就从写博客开始吧. 公司的项目一般都用微软的企业库,一直没时间好好研究,第一天上班还不忙,就抽空研究了下.搜索一下,发现这个企业库已经到了6 ...
- Android 开发工具类 34_OpenFileUtil
匹配文件后缀名 MIME 类型. import java.io.File; import android.content.Context; import android.content.Intent; ...
- Linux 一次杀死多进程
.- | 说明: “grep firefox”的输出结果是,所有含有关键字“firefox”的进程. “grep -v grep”是在列出的进程中去除含有关键字“grep”的进程. “-”是截取输入行 ...
- [转]DevOps解决方案-腾讯云
本文转自:https://cloud.tencent.com/solution/devops 什么是 DevOps? DevOps 集文化理念.实践和工具于一身,可以提高企业高速交付应用程序和服务 ...
- Mysql技能之【性能优化方案】
两个SHOW命令 Mysql服务状态 SHOW GLOBAL STATUS; Mysql配置信息 SHOW VARIABLES: 慢查询 show variables like '%slow%'; s ...