39 | * 需要3个权限(都是危险权限):
40 | * 1. 读取通讯录权限;
41 | * 2. 读取外部存储器权限;
42 | * 3. 写入外部存储器权限.
43 | */
44 | public void requestApplicationPermission() {
45 | /**
46 | * 第 1 步: 检查是否有相应的权限
47 | */
48 | boolean isAllGranted = checkPermissionAllGranted(requestPermissions);
49 | // 如果这3个权限全都拥有, 则直接执行备份代码
50 | if (isAllGranted) {
51 | if (mRequestPermissionListenner != null)
52 | mRequestPermissionListenner.requestSuccess();
53 | return;
54 | }
55 |
56 | /**
57 | * 第 2 步: 请求权限
58 | */
59 | // 一次请求多个权限, 如果其他有权限是已经授予的将会自动忽略掉
60 | ActivityCompat.requestPermissions(
61 | (Activity) mContext,
62 | requestPermissions,
63 | MY_PERMISSION_REQUEST_CODE
64 | );
65 | }
66 |
67 | /**
68 | * 检查是否拥有指定的所有权限
69 | */
70 | private boolean checkPermissionAllGranted(String[] permissions) {
71 | for (String permission : permissions) {
72 | if (ContextCompat.checkSelfPermission(mContext, permission) != PackageManager.PERMISSION_GRANTED) {
73 | // 只要有一个权限没有被授予, 则直接返回 false
74 | return false;
75 | }
76 | }
77 | return true;
78 | }
79 |
80 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
81 |
82 | if (requestCode == MY_PERMISSION_REQUEST_CODE) {
83 | boolean isAllGranted = true;
84 |
85 | // 判断是否所有的权限都已经授予了
86 | for (int grant : grantResults) {
87 | if (grant != PackageManager.PERMISSION_GRANTED) {
88 | isAllGranted = false;
89 | break;
90 | }
91 | }
92 |
93 | if (isAllGranted) {
94 | // 如果所有的权限都授予了, 则执行备份代码
95 | if (mRequestPermissionListenner != null){
96 | mRequestPermissionListenner.requestSuccess();
97 | }
98 | } else {
99 | // 弹出对话框告诉用户需要权限的原因, 并引导用户去应用权限管理中手动打开权限按钮
100 | openAppDetails();
101 | }
102 | }
103 | }
104 |
105 | /**
106 | * 打开 APP 的详情设置
107 | */
108 | private void openAppDetails() {
109 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
110 | builder.setMessage("缺少必要权限,请到 “应用信息 -> 权限” 中授予!");
111 | builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() {
112 | @Override
113 | public void onClick(DialogInterface dialog, int which) {
114 | Intent intent = new Intent();
115 | intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
116 | intent.addCategory(Intent.CATEGORY_DEFAULT);
117 | intent.setData(Uri.parse("package:" + mContext.getPackageName()));
118 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
119 | intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
120 | intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
121 | mContext.startActivity(intent);
122 | }
123 | });
124 | builder.setNegativeButton("取消", null);
125 | builder.show();
126 | }
127 |
128 | public interface RequestPermissionListenner {
129 | void requestSuccess();
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chezi008/videosurveillance/viedeoview/PlayerViewState.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.videosurveillance.viedeoview;
2 |
3 | /**
4 | * Created by Administrator on 2016/10/24.
5 | */
6 |
7 | public enum PlayerViewState {
8 | Default,
9 | Playying,
10 | Waiting,
11 | Control
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chezi008/videosurveillance/viedeoview/PqVideoDecoder.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.videosurveillance.viedeoview;
2 |
3 | import android.annotation.TargetApi;
4 | import android.graphics.SurfaceTexture;
5 | import android.media.MediaCodec;
6 | import android.media.MediaFormat;
7 | import android.os.Build;
8 | import android.support.annotation.RequiresApi;
9 | import android.util.Log;
10 | import android.view.Surface;
11 |
12 | import java.nio.ByteBuffer;
13 | import java.util.Arrays;
14 |
15 | /**
16 | * @author :chezi008 on 2017/11/29 12:34
17 | * @description :视频解码器
18 | * @email :chezi008@qq.com
19 | */
20 |
21 | public class PqVideoDecoder {
22 |
23 | public static final int NALU_TYPE_IDR = 5;
24 | public static final int NALU_TYPE_SPS = 7;
25 | public static final int NALU_TYPE_PPS = 8;
26 |
27 | private String TAG = getClass().getSimpleName();
28 | private MediaFormat mVideoFormat;
29 | private MediaCodec mVideoCodec;
30 | private Surface mSurface;
31 | private ByteBuffer[] mVideoInputBuffers;
32 |
33 | private long timeUs=10000;
34 | private long presentationTimeUs;
35 |
36 | public static byte[] header_sps;
37 | public static byte[] header_pps;
38 |
39 | /**
40 | * 初始化视频编码器
41 | *
42 | * @param surfaceTexture
43 | */
44 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
45 | public boolean init(SurfaceTexture surfaceTexture, int width, int height) {
46 | if (mVideoCodec == null) {
47 | try {
48 | mVideoFormat = MediaFormat.createVideoFormat("video/avc", width, height);
49 | // if (true) {
50 | // Log.d(TAG, "init: header_sps"+ Arrays.toString(header_sps));
51 | // byte[] sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 };
52 | // byte[] pps = { 0, 0, 0, 1, 104, -18, 60, -128 };
53 | // mVideoFormat.setByteBuffer("csd-0", ByteBuffer.wrap(sps));
54 | // mVideoFormat.setByteBuffer("csd-1", ByteBuffer.wrap(pps));
55 | // }
56 | // mVideoFormat.setInteger(MediaFormat.KEY_FRAME_RATE,5);
57 | // mVideoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,1);
58 | String mime = mVideoFormat.getString(MediaFormat.KEY_MIME);
59 | mVideoCodec = MediaCodec.createDecoderByType(mime);
60 | mSurface = new Surface(surfaceTexture);
61 | mVideoCodec.configure(mVideoFormat, mSurface, null, 0);
62 | mVideoCodec.start();
63 | } catch (Exception ex) {
64 | ex.printStackTrace();
65 | return false;
66 | }
67 | }
68 | return true;
69 | }
70 |
71 | private boolean isSetSpecificData;
72 |
73 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
74 | public void feedData(byte[] buf, int offset, int length) {
75 | int naluType = 0;
76 | if (length < 4) {
77 | return;
78 | }
79 | //判断帧的类型
80 | // Log.d(TAG, "feedData: type:"+(buf[4] & 0x1f));
81 | switch (buf[4] & 0x1f) {
82 | case NALU_TYPE_IDR:
83 | naluType = MediaCodec.BUFFER_FLAG_KEY_FRAME;
84 | break;
85 | case NALU_TYPE_SPS:
86 | isSetSpecificData = true;
87 | naluType = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
88 | header_sps = buf;
89 | break;
90 | case NALU_TYPE_PPS:
91 | naluType = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
92 | header_pps = buf;
93 | break;
94 | default:
95 | naluType = 0;
96 | break;
97 | }
98 | if (!isSetSpecificData) {
99 | Log.d(TAG, "feedData: return");
100 | return;
101 | }
102 | try {
103 | mVideoInputBuffers = mVideoCodec.getInputBuffers();
104 | // 这里解释一下 传0是不等待 传-1是一直等待 但是传-1会在很多机器上挂掉,所以还是用0吧 丢帧总比挂掉强
105 | int inputBufferIndex = mVideoCodec.dequeueInputBuffer(timeUs);
106 | if (inputBufferIndex >= 0) {
107 | // 从输入队列里去空闲buffer
108 | ByteBuffer inputBuffer = mVideoCodec.getInputBuffer(inputBufferIndex);
109 | inputBuffer.clear();
110 | inputBuffer.put(buf, offset, length);
111 | mVideoCodec.queueInputBuffer(inputBufferIndex, 0, length, presentationTimeUs * 1000000 / 25, naluType);
112 | presentationTimeUs ++;
113 | }
114 |
115 | MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
116 | //出列输出缓冲区,阻塞最多timeoutUs微妙
117 | int outputBufferIndex = mVideoCodec.dequeueOutputBuffer(bufferInfo, timeUs);
118 | // if (outputBufferIndex >= 0) {
119 | // // 将解码后数据渲染到surface上
120 | // boolean doRender = (bufferInfo.size != 0);
121 | // // doRender为true的时候意味着将数据显示在之前设置好的surfaceview上面
122 | // mVideoCodec.releaseOutputBuffer(outputBufferIndex, doRender);
123 | // } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
124 | // Log.d(TAG, "onDecodeVideoFrame: ----->outputBufferIndex:" + outputBufferIndex);
125 | // }
126 | while (outputBufferIndex>0){
127 | mVideoCodec.releaseOutputBuffer(outputBufferIndex, true);
128 | outputBufferIndex = mVideoCodec.dequeueOutputBuffer(bufferInfo, 0);
129 | }
130 | // switch (outputBufferIndex) {
131 | // case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
132 | // return ;
133 | // case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
134 | // return ;
135 | // case MediaCodec.INFO_TRY_AGAIN_LATER:
136 | // return ;
137 | // case MediaCodec.BUFFER_FLAG_KEY_FRAME:
138 | // return ;
139 | // default:
140 | // //show image right now
141 | // mVideoCodec.releaseOutputBuffer(outputBufferIndex, true);
142 | // return ;
143 | // }
144 | } catch (Exception ex) {
145 | ex.printStackTrace();
146 | }
147 | }
148 |
149 | public void stop() {
150 | if (mVideoCodec != null) {
151 | try {
152 | mVideoCodec.stop();
153 | mVideoCodec.release();
154 | mVideoCodec = null;
155 | } catch (IllegalStateException e) {
156 | e.printStackTrace();
157 | mVideoCodec = null;
158 | }
159 | }
160 |
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chezi008/videosurveillance/viedeoview/PqVideoPlayer.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.videosurveillance.viedeoview;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Color;
6 | import android.graphics.SurfaceTexture;
7 | import android.os.Handler;
8 | import android.os.Message;
9 | import android.os.SystemClock;
10 | import android.text.TextUtils;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.view.MotionEvent;
14 | import android.view.TextureView;
15 | import android.view.View;
16 | import android.view.ViewGroup;
17 | import android.widget.ImageView;
18 | import android.widget.LinearLayout;
19 | import android.widget.ProgressBar;
20 | import android.widget.RelativeLayout;
21 | import android.widget.TextView;
22 |
23 | import com.chezi008.videosurveillance.H264ReadRunable;
24 | import com.chezi008.videosurveillance.R;
25 | import com.chezi008.videosurveillance.ui.FullScreenActivity;
26 | import com.chezi008.videosurveillance.utils.DensityUtils;
27 |
28 | import java.util.concurrent.ArrayBlockingQueue;
29 | import java.util.concurrent.ScheduledExecutorService;
30 | import java.util.concurrent.ScheduledThreadPoolExecutor;
31 | import java.util.concurrent.ThreadPoolExecutor;
32 | import java.util.concurrent.TimeUnit;
33 |
34 | /**
35 | * 描述:自定义播放器
36 | *
37 | * @author :chezi008 on 2017/11/21 15:41
38 | * 邮箱:chezi008@163.com
39 | */
40 |
41 | public class PqVideoPlayer extends RelativeLayout implements View.OnClickListener, PqVideoPlayerZoomIn.PqZoomOutVideoListener {
42 |
43 | private static final int CORE_POOL_SIZE = 5;
44 | private static final int MAXIMUM_POOL_SIZE = 10;
45 | private static final int KEEP_ALIVE_TIME = 60000;
46 |
47 | public static final int HANDLER_SET_STATUS_DEAULT = 0;
48 | public static final int HANDLER_SET_STATUS_PLAYING = 1;
49 | public static final int HANDLER_UPDATE_BPS = 2;
50 | public static final int HANDLER_UPDATE_LOSS_FRAME = 3;
51 |
52 | private ArrayBlockingQueue mArrayBlockingQueue;
53 | private String TAG = getClass().getSimpleName();
54 | //自定义属性 1、选中时候边框大小 2、选中边框颜色
55 | //3、默认背景色 4、选中背景色
56 | // 5、资源添加icon
57 | /**
58 | * 默认边框大小
59 | */
60 | private final int DEFAULT_BORDER_SIZE = 1;
61 | private final int DEFALUT_BORDER_COLOR = Color.parseColor("#00000000");
62 | /**
63 | * 默认背景
64 | */
65 | private final int DEFAULT_BG_COLOR = Color.parseColor("#6b7e9e");
66 | private final int DEFAULT_BG_SELECT_COLOR = Color.parseColor("#FFFFFF");
67 | /**
68 | * 默认添加资源图片
69 | */
70 | private final int DEFAULT_RES_ICON = R.mipmap.vp_ic_res_add;
71 | private final int DEFAULT_FULL_SCREEN_ICON = R.mipmap.vp_ic_full_screen;
72 | private final int DEFAULT_STOP_ICON = R.mipmap.vp_ic_stop_play;
73 | /**
74 | * 选中边框
75 | */
76 | private int mBorderSize, mBorderColor;
77 | private int mBgColor, mBgSelectColor;
78 | private int mResIcon, mFullIcon, mStopIcon;
79 |
80 | private RelativeLayout mRlBorderBg, mRlVideoBg, mTextureParent, mRlBottomBar;
81 | private TextureView mTextureView;
82 | private SurfaceTexture mSurfaceTexture;
83 | private TextureView.SurfaceTextureListener mSurfaceTextureListener;
84 |
85 | private ProgressBar mProcessBar;
86 | private TextView mTvPath, mTvLoss;
87 | private ImageView ivResAdd;
88 |
89 | /**
90 | * 解码器
91 | */
92 | private PqVideoDecoder mVideoDecoder;
93 | /**
94 | * 线程池
95 | */
96 | private ThreadPoolExecutor mExecutorService;
97 | private ScheduledExecutorService mScheduledExecutorService;
98 | private Runnable mScheduleRunable;
99 | private H264ReadRunable mH264ReadRunable;
100 |
101 | private boolean isFullScreen, isFoucsed, isZoomIn;
102 | /**
103 | * 码流统计
104 | */
105 | private Handler mMainHandler;
106 |
107 | private VideoPlayerListener videoPlayerListener;
108 | private PqVideoOnclickListener mPqVideoOnclickListener;
109 |
110 | private PlayerViewState curState;
111 |
112 | public PqVideoPlayer(Context context) {
113 | this(context, null);
114 | }
115 |
116 | public PqVideoPlayer(Context context, AttributeSet attrs) {
117 | super(context, attrs);
118 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PqVideoPlayer);
119 | mBorderSize = ta.getInt(R.styleable.PqVideoPlayer_vp_border_size, DEFAULT_BORDER_SIZE);
120 | mBorderColor = ta.getColor(R.styleable.PqVideoPlayer_vp_border_color, DEFALUT_BORDER_COLOR);
121 | mBgColor = ta.getColor(R.styleable.PqVideoPlayer_vp_bg_default_color, DEFAULT_BG_COLOR);
122 | mBgSelectColor = ta.getColor(R.styleable.PqVideoPlayer_vp_bg_select_color, DEFAULT_BG_SELECT_COLOR);
123 | mResIcon = ta.getResourceId(R.styleable.PqVideoPlayer_vp_res_icon, DEFAULT_RES_ICON);
124 | mFullIcon = ta.getResourceId(R.styleable.PqVideoPlayer_vp_fullscreen_icon, DEFAULT_FULL_SCREEN_ICON);
125 | mStopIcon = ta.getResourceId(R.styleable.PqVideoPlayer_vp_stop_icon, DEFAULT_STOP_ICON);
126 |
127 | initVirable();
128 | initView();
129 | }
130 |
131 |
132 | public void setVideoPlayerListener(VideoPlayerListener videoPlayerListener) {
133 | this.videoPlayerListener = videoPlayerListener;
134 | }
135 |
136 | public void setPqVideoOnclickListener(PqVideoOnclickListener mPqVideoOnclickListener) {
137 | this.mPqVideoOnclickListener = mPqVideoOnclickListener;
138 | }
139 |
140 | private void initVirable() {
141 | mArrayBlockingQueue = new ArrayBlockingQueue(MAXIMUM_POOL_SIZE);
142 | mExecutorService = new ThreadPoolExecutor(CORE_POOL_SIZE,
143 | MAXIMUM_POOL_SIZE,
144 | KEEP_ALIVE_TIME,
145 | TimeUnit.MILLISECONDS, mArrayBlockingQueue);
146 | mScheduledExecutorService = new ScheduledThreadPoolExecutor(1);
147 |
148 | if (mMainHandler == null) {
149 | mMainHandler = new Handler() {
150 | @Override
151 | public void handleMessage(Message msg) {
152 | switch (msg.what) {
153 | case HANDLER_SET_STATUS_DEAULT:
154 | setDefaultUI();
155 | break;
156 | case HANDLER_SET_STATUS_PLAYING:
157 | setPlayerViewState(PlayerViewState.Playying);
158 | break;
159 | case HANDLER_UPDATE_BPS:
160 | // if (videoPlayerFullScreenListener != null) {
161 | // videoPlayerFullScreenListener.onGetBps((String) msg.obj);
162 | // }
163 | break;
164 | //播放器丢包率更新
165 | case HANDLER_UPDATE_LOSS_FRAME:
166 | mTvLoss.setText((String) msg.obj);
167 | // if (videoPlayerFullScreenListener != null) {
168 | // videoPlayerFullScreenListener.onGetLossFrame((String) msg.obj);
169 | // }
170 | break;
171 | default:
172 | break;
173 | }
174 | }
175 | };
176 | }
177 | }
178 |
179 | /**
180 | * 初始化视图
181 | */
182 | private void initView() {
183 | //设置屏幕常亮
184 | setKeepScreenOn(true);
185 | //添加选中的边框
186 | LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
187 | mRlBorderBg = new RelativeLayout(getContext());
188 | mRlBorderBg.setBackgroundColor(mBorderColor);
189 | mRlBorderBg.setPadding(mBorderSize, mBorderSize, mBorderSize, mBorderSize);
190 | addView(mRlBorderBg, layoutParams);
191 | //添加播放器背景
192 | layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
193 | mRlVideoBg = new RelativeLayout(getContext());
194 | mRlVideoBg.setBackgroundColor(mBgColor);
195 | mRlBorderBg.addView(mRlVideoBg, layoutParams);
196 |
197 | //添加播放器显示界面
198 | layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
199 | mTextureParent = new RelativeLayout(getContext());
200 | mTextureParent.setBackgroundColor(Color.BLACK);
201 | mTextureParent.setVisibility(GONE);
202 | mRlVideoBg.addView(mTextureParent, layoutParams);
203 |
204 | //添加添加资源图标
205 | layoutParams = new LayoutParams(DensityUtils.sp2px(getContext(), 35),
206 | DensityUtils.sp2px(getContext(), 35));
207 | layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
208 | ivResAdd = new ImageView(getContext());
209 | ivResAdd.setImageResource(mResIcon);
210 | ivResAdd.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
211 | ivResAdd.setId(R.id.iv_res_add);
212 | ivResAdd.setOnClickListener(this);
213 | mRlVideoBg.addView(ivResAdd, layoutParams);
214 | //添加progres
215 | mProcessBar = new ProgressBar(getContext(), null, android.R.attr.progressBarStyle);
216 | mRlVideoBg.addView(mProcessBar, layoutParams);
217 | mProcessBar.setVisibility(GONE);
218 |
219 | // addTextureView();
220 |
221 | //添加播放器控制栏
222 | //底部白色背景
223 | LayoutParams layoutParamsInner = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
224 | mRlBottomBar = new RelativeLayout(getContext());
225 | mRlBottomBar.setBackgroundColor(Color.WHITE);
226 | mRlBottomBar.getBackground().setAlpha(128);
227 | mRlBottomBar.setGravity(CENTER_VERTICAL);
228 | layoutParamsInner.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
229 | mRlVideoBg.addView(mRlBottomBar, layoutParamsInner);
230 | mRlBottomBar.setVisibility(GONE);
231 | //全屏
232 | int btnWidth = DensityUtils.sp2px(getContext(), 25);
233 | int btnPadding = DensityUtils.sp2px(getContext(), 3);
234 | LayoutParams layoutParamsFullScreen = new LayoutParams(btnWidth, btnWidth);
235 | layoutParamsFullScreen.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
236 | ImageView ivFull = new ImageView(getContext());
237 | ivFull.setId(R.id.btnFullScreen);
238 |
239 | ivFull.setPadding(btnPadding, btnPadding, btnPadding, btnPadding);
240 | ivFull.setImageResource(mFullIcon);
241 | ivFull.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
242 | ivFull.setOnClickListener(this);
243 | mRlBottomBar.addView(ivFull, layoutParamsFullScreen);
244 | //停止
245 | LayoutParams layoutParamsStopVideo = new LayoutParams(btnWidth, btnWidth);
246 | layoutParamsStopVideo.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
247 | ImageView ivStop = new ImageView(getContext());
248 | ivStop.setId(R.id.btnStop);
249 | ivStop.setPadding(btnPadding, btnPadding, btnPadding, btnPadding);
250 | ivStop.setImageResource(mStopIcon);
251 | ivStop.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
252 | ivStop.setOnClickListener(this);
253 | mRlBottomBar.addView(ivStop, layoutParamsStopVideo);
254 |
255 | //设备路径信息
256 | LinearLayout llPath = new LinearLayout(getContext());
257 | llPath.setId(R.id.tvDeviceInfo);
258 | mTvPath = new TextView(getContext());
259 | mTvPath.setTextColor(Color.WHITE);
260 | mTvPath.setTextSize(8);
261 | mTvPath.setSingleLine(true);
262 | mTvPath.setText("轮播字");
263 | mTvPath.setEllipsize(TextUtils.TruncateAt.MARQUEE);
264 | mTvPath.setMarqueeRepeatLimit(-1);
265 | mTvPath.setSelected(true);
266 | llPath.addView(mTvPath);
267 |
268 | LayoutParams layoutParamsDevicePathTextView = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
269 | layoutParamsDevicePathTextView.setMargins(btnPadding, btnPadding, btnPadding, btnPadding);
270 | layoutParamsDevicePathTextView.addRule(RIGHT_OF, R.id.btnStop);
271 | layoutParamsDevicePathTextView.addRule(RelativeLayout.LEFT_OF, R.id.tvLossFrame);
272 | layoutParamsDevicePathTextView.addRule(CENTER_VERTICAL);
273 | mRlBottomBar.addView(llPath, layoutParamsDevicePathTextView);
274 |
275 | //丢包率
276 | mTvLoss = new TextView(getContext());
277 | mTvLoss.setId(R.id.tvLossFrame);
278 | mTvLoss.setTextColor(Color.WHITE);
279 | mTvLoss.setTextSize(8);
280 | mTvLoss.setLines(1);
281 | mTvLoss.setText("0.00%");
282 | LayoutParams rlLoss = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
283 | rlLoss.setMargins(btnPadding, btnPadding, btnPadding, btnPadding);
284 | rlLoss.addRule(RelativeLayout.LEFT_OF, R.id.btnFullScreen);
285 | rlLoss.addRule(CENTER_VERTICAL);
286 | mRlBottomBar.addView(mTvLoss, rlLoss);
287 |
288 | setPlayerViewState(PlayerViewState.Default);
289 | // setPlayerViewState(PlayerViewState.Playying);
290 | }
291 |
292 | private void initTextureView() {
293 | if (mTextureView == null) {
294 | mTextureView = new VideoTextureView(getContext());
295 | mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
296 | @Override
297 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
298 | Log.d(TAG, "onSurfaceTextureAvailable: ");
299 | //检查解码器是否初始化
300 | if (!initDecoder(surface)) {
301 | Log.d(TAG, "onSurfaceTextureAvailable: init decoder failed!");
302 | return;
303 | }
304 | if (mH264ReadRunable != null) {
305 | Log.d(TAG, "onSurfaceTextureAvailable: 读取视频流");
306 | mExecutorService.execute(mH264ReadRunable);
307 | }
308 | }
309 |
310 | @Override
311 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
312 |
313 | }
314 |
315 | @Override
316 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
317 | Log.d(TAG, "onSurfaceTextureDestroyed: ");
318 | // stopDecoder();
319 | mSurfaceTexture = surface;
320 | if (isZoomIn) {
321 | videoPlayerListener.onChangeSurface(surface);
322 | }
323 | return false;
324 | }
325 |
326 | @Override
327 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
328 |
329 | }
330 | });
331 |
332 | }
333 | }
334 |
335 | /**
336 | * 添加显示画面
337 | */
338 | private void addTextureView() {
339 | Log.d(TAG, "addTextureView: ----------->");
340 | LayoutParams textureParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
341 | textureParams.addRule(RelativeLayout.CENTER_IN_PARENT);
342 | if (mTextureParent.getChildCount() > 0) {
343 | throw new IllegalStateException("已经添加了显示控件!");
344 | }
345 | //不能添加空控件 播放器setPlaying的时候会添加
346 | mTextureParent.addView(mTextureView, textureParams);
347 | mTextureParent.setVisibility(VISIBLE);
348 | }
349 |
350 |
351 | /**
352 | * 停止解码器
353 | */
354 | private void stopDecoder() {
355 | if (mVideoDecoder != null) {
356 | mVideoDecoder.stop();
357 | }
358 | }
359 |
360 | /**
361 | * 移除显示画面
362 | */
363 | private void removeTextureView() {
364 | mTextureParent.setVisibility(GONE);
365 | if (mTextureParent.getChildCount() > 0) {
366 | mTextureParent.removeView(mTextureView);
367 | // mTextureView = null;
368 | }
369 | }
370 |
371 | private void resetTextureView() {
372 | mTextureParent.setVisibility(GONE);
373 | if (mTextureParent.getChildCount() > 0) {
374 | mTextureParent.removeView(mTextureView);
375 | mTextureView = null;
376 | }
377 | }
378 |
379 |
380 | @Override
381 | public void onClick(View v) {
382 | if (v.getId() == R.id.iv_res_add) {
383 | //播放
384 | if (mH264ReadRunable == null) {
385 | mH264ReadRunable = new H264ReadRunable();
386 | mH264ReadRunable.setH264ReadListener(new H264ReadRunable.H264ReadListener() {
387 | @Override
388 | public void onFrameData(byte[] datas) {
389 | mVideoDecoder.feedData(datas, 0, datas.length);
390 | }
391 |
392 | @Override
393 | public void onStopRead() {
394 | stopVideoPlayer(true);
395 | }
396 | });
397 | }
398 | setPlayerViewState(PlayerViewState.Playying);
399 | } else if (v.getId() == R.id.btnFullScreen) {
400 | //全屏
401 | startFullScreen();
402 | } else if (v.getId() == R.id.btnStop) {
403 | //停止
404 | Log.d(TAG, "onClick:btnStop ");
405 | stopVideoPlayer(true);
406 | }
407 | setSelectStyle();
408 | }
409 |
410 | /**
411 | * 开启全屏
412 | */
413 | public void startFullScreen() {
414 | isFullScreen = true;
415 | FullScreenActivity.start(getContext());
416 | }
417 |
418 | /**
419 | * 退出全屏模式
420 | */
421 | public void exitFullScreen() {
422 | isFullScreen = false;
423 | }
424 |
425 |
426 | @Override
427 | public boolean onTouchEvent(MotionEvent event) {
428 | // gestureDetector.onTouchEvent(event);
429 | switch (event.getAction()) {
430 | case MotionEvent.ACTION_DOWN:
431 | doubleClick_2();
432 | mPqVideoOnclickListener.onClick();
433 | return true;
434 | case MotionEvent.ACTION_UP:
435 | return true;
436 | }
437 | return super.onTouchEvent(event);
438 | }
439 |
440 | private long[] mHits = new long[2];
441 |
442 | private void doubleClick_2() {
443 | System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
444 | //获取手机开机时间
445 | mHits[mHits.length - 1] = SystemClock.uptimeMillis();
446 | if (mHits[mHits.length - 1] - mHits[0] < 500) {
447 | /**双击的业务逻辑*/
448 | Log.d(TAG, "doubleClick_2: 双击");
449 | onZoomViewExchange();
450 | }
451 | }
452 |
453 | private void onZoomViewExchange() {
454 | if (mTextureView != null) {
455 | mSurfaceTextureListener = mTextureView.getSurfaceTextureListener();
456 | }
457 | isZoomIn = true;
458 | removeTextureView();
459 | videoPlayerListener.onZoomInView(PqVideoPlayer.this);
460 | }
461 |
462 | /**
463 | * 设置选中的状态
464 | */
465 | private void setSelectStyle() {
466 | isFoucsed = true;
467 | //背景为黑色,且出现选中边框
468 | mRlVideoBg.setBackgroundColor(Color.BLACK);
469 | mRlBorderBg.setBackgroundColor(mBgSelectColor);
470 | }
471 |
472 | private void setDefultStyle() {
473 | isFoucsed = false;
474 | mRlVideoBg.setBackgroundColor(mBgColor);
475 | mRlBorderBg.setBackgroundColor(mBorderColor);
476 | }
477 |
478 | /**
479 | * 设置时候获取焦点
480 | *
481 | * @param focus
482 | */
483 | public void setFocus(boolean focus) {
484 | if (focus) {
485 | setSelectStyle();
486 | } else {
487 | setDefultStyle();
488 | }
489 | }
490 |
491 | /**
492 | * 开始播放视频
493 | */
494 | public void startVideoPlayer() {
495 | setPlayerViewState(PlayerViewState.Playying);
496 | //设置轮播字幕
497 | // mTvPath.setText(mParams.getVideoName());
498 | }
499 |
500 | public void stopVideoPlayer(boolean isClear) {
501 | //停止视频解码器
502 | stopDecoder();
503 | //移除显示画面
504 | mMainHandler.sendEmptyMessage(HANDLER_SET_STATUS_DEAULT);
505 | // setPlayerViewState(PlayerViewState.Default);
506 | if (isClear) {
507 | // mParams.getSessionParams().requestId = null;
508 | }
509 | }
510 |
511 | /**
512 | * 显示加载动画
513 | */
514 | public void showProgressbar() {
515 | setPlayerViewState(PlayerViewState.Waiting);
516 | //定时器 15s还没来自动关闭
517 | if (mScheduleRunable == null) {
518 | mScheduleRunable = new Runnable() {
519 | @Override
520 | public void run() {
521 | if (mProcessBar.getVisibility() == VISIBLE) {
522 | mMainHandler.sendEmptyMessage(HANDLER_SET_STATUS_DEAULT);
523 | }
524 | }
525 | };
526 | }
527 | mScheduledExecutorService.schedule(mScheduleRunable, 15000, TimeUnit.MILLISECONDS);
528 | }
529 |
530 |
531 | private void setPlayerViewState(PlayerViewState state) {
532 | curState = state;
533 | Log.d(TAG, "setPlayerViewState: " + curState);
534 | switch (state) {
535 | case Default:
536 | //默认背景
537 | mRlVideoBg.setBackgroundColor(mBgColor);
538 | ivResAdd.setVisibility(VISIBLE);
539 | //隐藏textureView
540 | resetTextureView();
541 | //隐藏dialog
542 | mProcessBar.setVisibility(GONE);
543 | //隐藏底部控制栏
544 | mRlBottomBar.setVisibility(GONE);
545 | break;
546 | case Waiting:
547 | //背景设为黑色
548 | mRlVideoBg.setBackgroundColor(Color.BLACK);
549 | //隐藏资源图标
550 | ivResAdd.setVisibility(GONE);
551 | //显示dialog
552 | mProcessBar.setVisibility(VISIBLE);
553 | break;
554 | case Playying:
555 | //隐藏dialog
556 | mProcessBar.setVisibility(GONE);
557 | //隐藏资源
558 | ivResAdd.setVisibility(GONE);
559 | initTextureView();
560 | addTextureView();
561 | //显示底部控制栏
562 | mRlBottomBar.setVisibility(VISIBLE);
563 | break;
564 | case Control:
565 | break;
566 | }
567 | }
568 |
569 |
570 | private boolean initDecoder(SurfaceTexture surface) {
571 | Log.d(TAG, "initDecoder: ");
572 | if (mVideoDecoder == null) {
573 | mVideoDecoder = new PqVideoDecoder();
574 | }
575 | boolean initSuccess = mVideoDecoder.init(surface,
576 | 1280,
577 | 720);
578 |
579 | return initSuccess;
580 | }
581 |
582 |
583 | private void setDefaultUI() {
584 | //判断当前是否是播放状态
585 | if (mRlBottomBar.getVisibility() == VISIBLE || mProcessBar.getVisibility() == VISIBLE) {
586 | //停止视频解码器
587 | stopDecoder();
588 | //移除显示画面
589 | setPlayerViewState(PlayerViewState.Default);
590 | setSelectStyle();
591 | }
592 | }
593 |
594 | @Override
595 | public void onZoomIn() {
596 | onResume();
597 | setVisibility(VISIBLE);
598 | isZoomIn = false;
599 | }
600 |
601 |
602 | public interface VideoPlayerListener {
603 | /**
604 | * 双击放大
605 | *
606 | * @param videoPlayer
607 | */
608 | void onZoomInView(PqVideoPlayer videoPlayer);
609 |
610 | /**
611 | * 渲染画面被交换
612 | *
613 | * @param surfaceTexture
614 | */
615 | void onChangeSurface(SurfaceTexture surfaceTexture);
616 |
617 | }
618 |
619 | public void onResume() {
620 | if (curState == PlayerViewState.Playying) {
621 | setSurfaceTexture();
622 | }
623 | }
624 |
625 | private void setSurfaceTexture() {
626 | if (null == mTextureView.getSurfaceTexture()) {
627 | Log.d(TAG, "setSurfaceTexture: ");
628 | mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
629 | mTextureView.setSurfaceTexture(mSurfaceTexture);
630 | }
631 | addTextureView();
632 | }
633 |
634 | public SurfaceTexture getSurfaceTexture() {
635 | return mTextureView.getSurfaceTexture();
636 | }
637 |
638 | @Override
639 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
640 | setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
641 | getDefaultSize(0, heightMeasureSpec));
642 |
643 | int childWidthSize = getMeasuredWidth();
644 | // 高度和宽度一样
645 | heightMeasureSpec = widthMeasureSpec = MeasureSpec.makeMeasureSpec(
646 | childWidthSize, MeasureSpec.EXACTLY);
647 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
648 | }
649 |
650 | public interface PqVideoOnclickListener {
651 | void onClick();
652 | }
653 | }
654 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chezi008/videosurveillance/viedeoview/PqVideoPlayerZoomIn.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.videosurveillance.viedeoview;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.graphics.SurfaceTexture;
6 | import android.os.SystemClock;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.text.TextUtils;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 | import android.view.Gravity;
13 | import android.view.MotionEvent;
14 | import android.view.TextureView;
15 | import android.view.View;
16 | import android.view.ViewGroup;
17 | import android.widget.FrameLayout;
18 | import android.widget.ImageView;
19 | import android.widget.LinearLayout;
20 | import android.widget.RelativeLayout;
21 | import android.widget.TextView;
22 |
23 | import com.chezi008.videosurveillance.R;
24 | import com.chezi008.videosurveillance.utils.DensityUtils;
25 |
26 | /**
27 | * @author :chezi008 on 2017/12/28 16:30
28 | * @description :
29 | * @email :chezi008@163.com
30 | */
31 |
32 | public class PqVideoPlayerZoomIn extends FrameLayout implements View.OnClickListener {
33 | private String TAG = getClass().getSimpleName();
34 |
35 | private RelativeLayout mTextureParent, mRlBottomBar;
36 | private TextureView mTextureView;
37 |
38 | private ImageView ivResAdd;
39 |
40 | private TextView mTvPath, mTvLoss;
41 |
42 | private PqZoomOutVideoListener mPqZoomOutVideoListener;
43 | private SurfaceTexture mSurfaceTexture;
44 |
45 | public PqVideoPlayerZoomIn(@NonNull Context context) {
46 | this(context, null);
47 | }
48 |
49 |
50 | public PqVideoPlayerZoomIn(@NonNull Context context, @Nullable AttributeSet attrs) {
51 | this(context, attrs, 0);
52 | }
53 |
54 | public PqVideoPlayerZoomIn(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
55 | super(context, attrs, defStyleAttr);
56 | initView();
57 | }
58 |
59 | private void initView() {
60 | //设置屏幕常亮
61 | setKeepScreenOn(true);
62 | setBackgroundColor(getResources().getColor(R.color.colorPrimary));
63 | addTextureParent();
64 | // addTextureView();
65 | addResIconView();
66 | addBottomView();
67 | }
68 |
69 | private void addBottomView() {
70 | //底部白色背景
71 | LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
72 | params.gravity = Gravity.BOTTOM;
73 | mRlBottomBar = new RelativeLayout(getContext());
74 | mRlBottomBar.setBackgroundColor(Color.WHITE);
75 | mRlBottomBar.getBackground().setAlpha(128);
76 | mRlBottomBar.setGravity(RelativeLayout.CENTER_VERTICAL);
77 | addView(mRlBottomBar, params);
78 |
79 | mRlBottomBar.setVisibility(VISIBLE);
80 | //全屏
81 | int btnWidth = DensityUtils.sp2px(getContext(), 25);
82 | int btnPadding = DensityUtils.sp2px(getContext(), 3);
83 | RelativeLayout.LayoutParams layoutParamsFullScreen = new RelativeLayout.LayoutParams(btnWidth, btnWidth);
84 | layoutParamsFullScreen.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
85 | ImageView ivFull = new ImageView(getContext());
86 | ivFull.setId(R.id.btnFullScreen);
87 |
88 | ivFull.setPadding(btnPadding, btnPadding, btnPadding, btnPadding);
89 | ivFull.setImageResource(R.mipmap.vp_ic_full_screen);
90 | ivFull.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
91 | ivFull.setOnClickListener(this);
92 | mRlBottomBar.addView(ivFull, layoutParamsFullScreen);
93 | //停止
94 | RelativeLayout.LayoutParams layoutParamsStopVideo = new RelativeLayout.LayoutParams(btnWidth, btnWidth);
95 | layoutParamsStopVideo.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
96 | ImageView ivStop = new ImageView(getContext());
97 | ivStop.setId(R.id.btnStop);
98 | ivStop.setPadding(btnPadding, btnPadding, btnPadding, btnPadding);
99 | ivStop.setImageResource(R.mipmap.vp_ic_stop_play);
100 | ivStop.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
101 | ivStop.setOnClickListener(this);
102 | mRlBottomBar.addView(ivStop, layoutParamsStopVideo);
103 |
104 | //设备路径信息
105 | LinearLayout llPath = new LinearLayout(getContext());
106 | llPath.setId(R.id.tvDeviceInfo);
107 | mTvPath = new TextView(getContext());
108 | mTvPath.setTextColor(Color.WHITE);
109 | mTvPath.setTextSize(8);
110 | mTvPath.setSingleLine(true);
111 | mTvPath.setText("轮播字");
112 | mTvPath.setEllipsize(TextUtils.TruncateAt.MARQUEE);
113 | mTvPath.setMarqueeRepeatLimit(-1);
114 | mTvPath.setSelected(true);
115 | llPath.addView(mTvPath);
116 |
117 | RelativeLayout.LayoutParams layoutParamsDevicePathTextView = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
118 | layoutParamsDevicePathTextView.setMargins(btnPadding, btnPadding, btnPadding, btnPadding);
119 | layoutParamsDevicePathTextView.addRule(RelativeLayout.RIGHT_OF, R.id.btnStop);
120 | layoutParamsDevicePathTextView.addRule(RelativeLayout.LEFT_OF, R.id.tvLossFrame);
121 | layoutParamsDevicePathTextView.addRule(RelativeLayout.CENTER_VERTICAL);
122 | mRlBottomBar.addView(llPath, layoutParamsDevicePathTextView);
123 |
124 | //丢包率
125 | mTvLoss = new TextView(getContext());
126 | mTvLoss.setId(R.id.tvLossFrame);
127 | mTvLoss.setTextColor(Color.WHITE);
128 | mTvLoss.setTextSize(8);
129 | mTvLoss.setLines(1);
130 | mTvLoss.setText("0.00%");
131 | RelativeLayout.LayoutParams rlLoss = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
132 | rlLoss.setMargins(btnPadding, btnPadding, btnPadding, btnPadding);
133 | rlLoss.addRule(RelativeLayout.LEFT_OF, R.id.btnFullScreen);
134 | rlLoss.addRule(RelativeLayout.CENTER_VERTICAL);
135 | mRlBottomBar.addView(mTvLoss, rlLoss);
136 |
137 | // mRlBorderBg.addView(mRlVideoBg, layoutParams);
138 | }
139 |
140 | private void addTextureParent() {
141 | //先添加其父容器
142 | LayoutParams parentParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
143 | parentParams.gravity = Gravity.CENTER;
144 |
145 | mTextureParent = new RelativeLayout(getContext());
146 | mTextureParent.setBackgroundColor(Color.BLACK);
147 | mTextureParent.setVisibility(VISIBLE);
148 | addView(mTextureParent, parentParams);
149 | }
150 |
151 | private void addResIconView() {
152 | //添加添加资源图标
153 | LayoutParams layoutParams = new LayoutParams(DensityUtils.sp2px(getContext(), 35),
154 | DensityUtils.sp2px(getContext(), 35));
155 | layoutParams.gravity = Gravity.CENTER;
156 | ivResAdd = new ImageView(getContext());
157 | ivResAdd.setImageResource(R.mipmap.vp_ic_res_add);
158 | ivResAdd.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
159 | ivResAdd.setId(R.id.iv_res_add);
160 | ivResAdd.setOnClickListener(this);
161 | addView(ivResAdd, layoutParams);
162 | }
163 |
164 | private void addTextureView() {
165 | //添加渲染视图
166 | if (mTextureView == null) {
167 | mTextureView = new VideoTextureView(getContext());
168 | mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
169 | @Override
170 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
171 | Log.d(TAG, "onSurfaceTextureAvailable: ");
172 | if(mSurfaceTexture!=null){
173 | mTextureView.setSurfaceTexture(mSurfaceTexture);
174 | }
175 | }
176 |
177 | @Override
178 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
179 |
180 | }
181 |
182 | @Override
183 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
184 | Log.d(TAG, "onSurfaceTextureDestroyed: ");
185 | mSurfaceTexture = surface;
186 | return false;
187 | }
188 |
189 | @Override
190 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
191 |
192 | }
193 | });
194 | }
195 |
196 | if (mTextureParent.getChildCount() > 0) {
197 | throw new IllegalStateException("已经添加了一个子控件了!");
198 | }
199 | RelativeLayout.LayoutParams LinlayoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
200 | RelativeLayout.LayoutParams.MATCH_PARENT);
201 | LinlayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
202 | mTextureParent.addView(mTextureView, LinlayoutParams);
203 | }
204 |
205 | public void setSurfaceView(SurfaceTexture surfaceView) {
206 | Log.d(TAG, "setSurfaceView: ");
207 | addTextureView();
208 | mTextureView.setSurfaceTexture(surfaceView);
209 | }
210 |
211 | public void setPqZoomOutVideoListener(PqZoomOutVideoListener mPqZoomOutVideoListener) {
212 | this.mPqZoomOutVideoListener = mPqZoomOutVideoListener;
213 | }
214 |
215 | @Override
216 | public boolean onTouchEvent(MotionEvent event) {
217 | switch (event.getAction()) {
218 | case MotionEvent.ACTION_DOWN:
219 | doubleClick_2();
220 | return true;
221 | case MotionEvent.ACTION_UP:
222 | return true;
223 | }
224 | return super.onTouchEvent(event);
225 | }
226 |
227 | private long[] mHits = new long[2];
228 |
229 | private void doubleClick_2() {
230 | System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
231 | //获取手机开机时间
232 | mHits[mHits.length - 1] = SystemClock.uptimeMillis();
233 | if (mHits[mHits.length - 1] - mHits[0] < 500) {
234 | /**双击的业务逻辑*/
235 | Log.d(TAG, "doubleClick_2: 双击");
236 | onZoomViewExchange();
237 | }
238 | }
239 |
240 | private void onZoomViewExchange() {
241 | mTextureParent.removeView(mTextureView);
242 | mPqZoomOutVideoListener.onZoomIn();
243 | setVisibility(GONE);
244 | }
245 |
246 | @Override
247 | public void onClick(View v) {
248 | switch (v.getId()) {
249 | case R.id.iv_res_add:
250 | break;
251 | }
252 | }
253 |
254 | public interface PqZoomOutVideoListener {
255 | void onZoomIn();
256 | }
257 |
258 | }
259 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chezi008/videosurveillance/viedeoview/VideoContainer.java:
--------------------------------------------------------------------------------
1 | package com.chezi008.videosurveillance.viedeoview;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.SurfaceTexture;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.widget.GridLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.support.v7.widget.helper.ItemTouchHelper;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.view.Gravity;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.widget.FrameLayout;
17 |
18 | import com.chezi008.videosurveillance.R;
19 | import com.chezi008.videosurveillance.VideoGridLayoutManager;
20 | import com.chezi008.videosurveillance.adapter.ContainerAdapter;
21 |
22 | import java.util.ArrayList;
23 | import java.util.Collections;
24 | import java.util.List;
25 |
26 | /**
27 | * @author :chezi008 on 2017/12/29 10:35
28 | * @description :
29 | * @email :chezi008@163.com
30 | */
31 |
32 | public class VideoContainer extends FrameLayout {
33 |
34 | private String TAG = getClass().getSimpleName();
35 | private static final int DEFALT_COLUMNS_NUM = 2;
36 |
37 | private VideoRecycler mVideoRecycler;
38 | private ItemTouchHelper mItemTouchHelper;
39 | private VideoGridLayoutManager mGridLayoutManager;
40 | private ContainerAdapter mAdapter;
41 | /**
42 | * 放大后的播放器
43 | */
44 | private PqVideoPlayerZoomIn mPqVideoPlayerZoomIn;
45 |
46 | private List