本文為用 MediaRecorder 錄制小視頻的控件,可以設置視頻錄制的時間、空間大小、初始時是否打開攝像頭等。 此控件為組合控件,繼承自 LinearLayout ,為防止出錯,需實現 android.media.MediaRecorder.OnErrorListener 接口。
小視頻錄制界面
MovieRecorderView.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
|
import java.io.File; import java.io.IOException; import java.util.Timer; import java.util.TimerTask; import android.content.Context; import android.content.res.TypedArray; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.media.MediaRecorder; import android.media.MediaRecorder.AudioEncoder; import android.media.MediaRecorder.AudioSource; import android.media.MediaRecorder.OnErrorListener; import android.media.MediaRecorder.OutputFormat; import android.media.MediaRecorder.VideoEncoder; import android.media.MediaRecorder.VideoSource; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.widget.LinearLayout; import android.widget.ProgressBar; import com.contron.dgyj.R; import com.contron.dgyj.common.Globals; import com.contron.dgyj.im.ImGlobal; import com.lidroid.xutils.util.LogUtils; /** * 視頻播放控件 * * @author liuyinjun * * @date 2015-2-5 */ public class MovieRecorderView extends LinearLayout implements OnErrorListener { private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private ProgressBar mProgressBar; private MediaRecorder mMediaRecorder; private Camera mCamera; private Timer mTimer; // 計時器 private OnRecordFinishListener mOnRecordFinishListener; // 錄制完成回調接口 private int mWidth; // 視頻分辨率寬度 private int mHeight; // 視頻分辨率高度 private boolean isOpenCamera; // 是否一開始就打開攝像頭 private int mRecordMaxTime; // 一次拍攝最長時間 private int mTimeCount; // 時間計數 private File mVecordFile = null ; // 文件 public MovieRecorderView(Context context) { this (context, null ); } public MovieRecorderView(Context context, AttributeSet attrs) { this (context, attrs, 0 ); } public MovieRecorderView(Context context, AttributeSet attrs, int defStyle) { super (context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MovieRecorderView, defStyle, 0 ); mWidth = a.getInteger(R.styleable.MovieRecorderView_width, 320 ); // 默認320 mHeight = a.getInteger(R.styleable.MovieRecorderView_height, 240 ); // 默認240 isOpenCamera = a.getBoolean(R.styleable.MovieRecorderView_is_open_camera, true ); // 默認打開 mRecordMaxTime = a.getInteger(R.styleable.MovieRecorderView_record_max_time, 10 ); // 默認為10 LayoutInflater.from(context).inflate(R.layout.movie_recorder_view, this ); mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview); mProgressBar = (ProgressBar) findViewById(R.id.progressBar); mProgressBar.setMax(mRecordMaxTime); // 設置進度條最大量 mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback( new CustomCallBack()); mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); a.recycle(); } /** * * @author liuyinjun * * @date 2015-2-5 */ private class CustomCallBack implements Callback { @Override public void surfaceCreated(SurfaceHolder holder) { if (!isOpenCamera) return ; try { initCamera(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (!isOpenCamera) return ; freeCameraResource(); } } /** * 初始化攝像頭 * * @author liuyinjun * @date 2015-2-5 * @throws IOException */ private void initCamera() throws IOException { if (mCamera != null ) { freeCameraResource(); } try { mCamera = Camera.open(); } catch (Exception e) { e.printStackTrace(); freeCameraResource(); } if (mCamera == null ) return ; setCameraParams(); mCamera.setDisplayOrientation( 90 ); mCamera.setPreviewDisplay(mSurfaceHolder); mCamera.startPreview(); mCamera.unlock(); } /** * 設置攝像頭為豎屏 * * @author liuyinjun * @date 2015-2-5 */ private void setCameraParams() { if (mCamera != null ) { Parameters params = mCamera.getParameters(); params.set( "orientation" , "portrait" ); mCamera.setParameters(params); } } /** * 釋放攝像頭資源 * * @author liuyinjun * @date 2015-2-5 */ private void freeCameraResource() { if (mCamera != null ) { mCamera.setPreviewCallback( null ); mCamera.stopPreview(); mCamera.lock(); mCamera.release(); mCamera = null ; } } private void createRecordDir() { File sampleDir = new File(Environment.getExternalStorageDirectory() + File.separator + "im/video/" ); if (!sampleDir.exists()) { sampleDir.mkdirs(); } File vecordDir = sampleDir; // 創建文件 try { mVecordFile = File.createTempFile( "recording" , ".mp4" , vecordDir); //mp4格式 LogUtils.i(mVecordFile.getAbsolutePath()); } catch (IOException e) { } } /** * 初始化 * * @author liuyinjun * @date 2015-2-5 * @throws IOException */ private void initRecord() throws IOException { mMediaRecorder = new MediaRecorder(); mMediaRecorder.reset(); if (mCamera != null ) mMediaRecorder.setCamera(mCamera); mMediaRecorder.setOnErrorListener( this ); mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); mMediaRecorder.setVideoSource(VideoSource.CAMERA); // 視頻源 mMediaRecorder.setAudioSource(AudioSource.MIC); // 音頻源 mMediaRecorder.setOutputFormat(OutputFormat.MPEG_4); // 視頻輸出格式 mMediaRecorder.setAudioEncoder(AudioEncoder.AMR_NB); // 音頻格式 mMediaRecorder.setVideoSize(mWidth, mHeight); // 設置分辨率: // mMediaRecorder.setVideoFrameRate(16);// 這個我把它去掉了,感覺沒什么用 mMediaRecorder.setVideoEncodingBitRate( 1 * 1024 * 512 ); // 設置幀頻率,然后就清晰了 mMediaRecorder.setOrientationHint( 90 ); // 輸出旋轉90度,保持豎屏錄制 mMediaRecorder.setVideoEncoder(VideoEncoder.MPEG_4_SP); // 視頻錄制格式 // mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000); mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath()); mMediaRecorder.prepare(); try { mMediaRecorder.start(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (RuntimeException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 開始錄制視頻 * * @author liuyinjun * @date 2015-2-5 * @param fileName * 視頻儲存位置 * @param onRecordFinishListener * 達到指定時間之后回調接口 */ public void record( final OnRecordFinishListener onRecordFinishListener) { this .mOnRecordFinishListener = onRecordFinishListener; createRecordDir(); try { if (!isOpenCamera) // 如果未打開攝像頭,則打開 initCamera(); initRecord(); mTimeCount = 0 ; // 時間計數器重新賦值 mTimer = new Timer(); mTimer.schedule( new TimerTask() { @Override public void run() { // TODO Auto-generated method stub mTimeCount++; mProgressBar.setProgress(mTimeCount); // 設置進度條 if (mTimeCount == mRecordMaxTime) { // 達到指定時間,停止拍攝 stop(); if (mOnRecordFinishListener != null ) mOnRecordFinishListener.onRecordFinish(); } } }, 0 , 1000 ); } catch (IOException e) { e.printStackTrace(); } } /** * 停止拍攝 * * @author liuyinjun * @date 2015-2-5 */ public void stop() { stopRecord(); releaseRecord(); freeCameraResource(); } /** * 停止錄制 * * @author liuyinjun * @date 2015-2-5 */ public void stopRecord() { mProgressBar.setProgress( 0 ); if (mTimer != null ) mTimer.cancel(); if (mMediaRecorder != null ) { // 設置后不會崩 mMediaRecorder.setOnErrorListener( null ); mMediaRecorder.setPreviewDisplay( null ); try { mMediaRecorder.stop(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (RuntimeException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } /** * 釋放資源 * * @author liuyinjun * @date 2015-2-5 */ private void releaseRecord() { if (mMediaRecorder != null ) { mMediaRecorder.setOnErrorListener( null ); try { mMediaRecorder.release(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } mMediaRecorder = null ; } public int getTimeCount() { return mTimeCount; } /** * @return the mVecordFile */ public File getmVecordFile() { return mVecordFile; } /** * 錄制完成回調接口 * * @author liuyinjun * * @date 2015-2-5 */ public interface OnRecordFinishListener { public void onRecordFinish(); } @Override public void onError(MediaRecorder mr, int what, int extra) { try { if (mr != null ) mr.reset(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } |
movie_recorder_view.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" android:background = "@android:color/background_dark" android:orientation = "vertical" > < SurfaceView android:id = "@+id/surfaceview" android:layout_width = "fill_parent" android:layout_height = "0dp" android:layout_weight = "1" /> < ProgressBar android:id = "@+id/progressBar" style = "?android:attr/progressBarStyleHorizontal" android:layout_width = "match_parent" android:layout_height = "2dp" /> </ LinearLayout > |
attrs.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" android:background = "@android:color/background_dark" android:orientation = "vertical" > < SurfaceView android:id = "@+id/surfaceview" android:layout_width = "fill_parent" android:layout_height = "0dp" android:layout_weight = "1" /> < ProgressBar android:id = "@+id/progressBar" style = "?android:attr/progressBarStyleHorizontal" android:layout_width = "match_parent" android:layout_height = "2dp" /> </ LinearLayout > |
以上所述就是本文的全部內容了,希望大家能夠喜歡。