public interface VideoController extends OnShutterButtonListener, OnPauseButtonListener {
public void onReviewDoneClicked(View view);
public void onReviewCancelClicked(View viwe);
public void onReviewPlayClicked(View view);
public boolean isVideoCaptureIntent();
public boolean isInReviewMode();
public int onZoomChanged(int index);
public void onSingleTapUp(View view, int x, int y);
public void stopPreview();
public void updateCameraOrientation();
// Callbacks for camera preview UI events.
public void onPreviewUIReady();
public void onPreviewUIDestroyed();
}
这个好像是video 主要去实现的功能,由于是在录像中点击屏幕拍照的 我猜想是 onSingleTapUp(View view, int x, int y) 这个方法才对,继续看下一个类
public class VideoModule implements CameraModule,VideoController,
CameraPreference.OnPreferenceChangedListener,
ShutterButton.OnShutterButtonListener,
MediaRecorder.OnErrorListener,
MediaRecorder.OnInfoListener {
}
这个类貌似实现了上面的接口 , 找到里面的方法
// SingleTapListener
// Preview area is touched. Take a picture.
@Override
public void onSingleTapUp(View view, int x, int y) {
if (mMediaRecorderPausing) return;
takeASnapshot();
}
private void takeASnapshot() {
if (mParameters == null) return;
// Only take snapshots if video snapshot is supported by device
if (CameraUtil.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent
&& !is4KEnabled()) {
if (!mMediaRecorderRecording || mPaused || mSnapshotInProgress) {
return;
}
MediaSaveService s = mActivity.getMediaSaveService();
if (s == null || s.isQueueFull()) {
return;
}
// Set rotation and gps data.
int rotation = CameraUtil.getJpegRotation(mCameraId, mOrientation);
mParameters.setRotation(rotation);
Location loc = mLocationManager.getCurrentLocation();
CameraUtil.setGpsParameters(mParameters, loc);
mCameraDevice.setParameters(mParameters);
Log.v(TAG, "Video snapshot start");
mCameraDevice.takePicture(mHandler,
null, null, null, new JpegPictureCallback(loc));
showVideoSnapshotUI(true);
mSnapshotInProgress = true;
UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
UsageStatistics.ACTION_CAPTURE_DONE, "VideoSnapshot");
}
}
很高兴的发现竟然找到了这个拍照的方法, 想要进入这个方法貌似是要看硬件是否支持这种拍照方式 也就是这个方法 CameraUtil.isVideoSnapshotSupported(mParameters)
在CameraUtil中找到这个 这个方法
public static final String TRUE = "true";
public static boolean isVideoSnapshotSupported(Parameters params) {
return TRUE.equals(params.get(VIDEO_SNAPSHOT_SUPPORTED));
}
如果你的设备如果支持的话就可以接着往下看了,
private CameraProxy mCameraDevice;类名
mCameraDevice.takePicture(mHandler,
null, null, null, new JpegPictureCallback(loc));
就是拍照的方法 继续往下找
public interface CameraProxy {
}
苦苦找到的结果竟然告诉我这是一个借口类 而且还是一个内部类, 没办法只能接着往下看
public class AndroidCameraProxyImpl implements CameraManager.CameraProxy {
@Override
public void takePicture(Handler handler,CameraShutterCallback shutter,CameraPictureCallback raw,CameraPictureCallback post,CameraPictureCallback jpeg) {
mCameraHandler.requestTakePicture(
ShutterCallbackForward.getNewInstance(handler, this, shutter),
PictureCallbackForward.getNewInstance(handler, this, raw),
PictureCallbackForward.getNewInstance(handler, this, post),
PictureCallbackForward.getNewInstance(handler, this, jpeg));
}
}
可算找到了竟然又把这个事件交给了handler来处理,接着找
public void requestTakePicture(
final ShutterCallback shutter,
final PictureCallback raw,
final PictureCallback postView,
final PictureCallback jpeg) {
post(new Runnable() {
@Override
public void run() {
try {
mCamera.takePicture(shutter, raw, postView, jpeg);
} catch (RuntimeException e) {
// TODO: output camera state and focus state for debugging.
Log.e(TAG, "take picture failed.");
throw e;
}
}
});
}
找来找去竟然告诉我直接用 mCamera.takePicture(shutter, raw, postView, jpeg); 本人亲自测试这样方法是行不通的啊,问题出在哪里,没办法继续去看看他的UI实现类
public class VideoUI implements PieRenderer.PieListener,
PreviewGestures.SingleTapListener,
CameraRootView.MyDisplayListener,
SurfaceTextureListener, SurfaceHolder.Callback,
PauseButton.OnPauseButtonListener {
public VideoUI(CameraActivity activity, VideoController controller, View parent) {
mActivity = activity;
mController = controller;
mRootView = parent;
mActivity.getLayoutInflater().inflate(R.layout.video_module, (ViewGroup) mRootView, true);
mPreviewCover = mRootView.findViewById(R.id.preview_cover);
<span style="color:#FF0000;">mTextureView = (TextureView) mRootView.findViewById(R.id.preview_content);</span>
mTextureView.setSurfaceTextureListener(this);
mTextureView.addOnLayoutChangeListener(mLayoutListener);
mFlashOverlay = mRootView.findViewById(R.id.flash_overlay);
mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);
mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher);
mSwitcher.setCurrentIndex(ModuleSwitcher.VIDEO_MODULE_INDEX);
mSwitcher.setSwitchListener(mActivity);
initializeMiscControls();
initializeControlByIntent();
initializeOverlay();
initializePauseButton();
mAnimationManager = new AnimationManager();
mOrientationResize = false;
mPrevOrientationResize = false;
}
}
这个类主要就是就是初始化一下控件,调用各个实现类来处理各种操作,但是我发现一个有趣的地方,在camera2中视频部分是用serfaceview和textuerview两个类来实现的,但是这个textureview是个什么鬼,是不是在调用Camera的时候把图像交给他就不会出现界面卡死的情况,那么surfaceview在mediaercord 的录制过程中有什么用呢, 各种查找文档他只不过做了一些界面的预览,其实和真正的录制没有太多关系,但是只要停止预览,录像就会停止!
只能去试一下这个textureview能不能实现使用mediarecord 一边录制一边拍照的功能了, 在我写的过程中发现
使用surfaceview中想要mediarecord将预览画面显示出来必须调用
prMediaRecorder.setPreviewDisplay(prSurfaceHolder.getSurface());这个方法 而textureview则不用,貌似就是这里的问题了
写完这个demo的时候一测试果然可行,顿时感觉神清气爽.但是经过我反复测试发现如果最开始在没有使用录像的过程中就调用camera.takepic的这个方法还是会到时界面卡死,但是只要使用过一次录像功能,不管是否正在录像都不会出现卡死的情况,这让我百思不得骑姐啊,没办法只能打印一下系统日志看一下,
C:\Users\Administrator>adb root
adb server is out of date. killing...
* daemon started successfully *
adbd is already running as root
C:\Users\Administrator>adb remount
remount succeeded
C:\Users\Administrator>adb shell
root@M9PLUS:/ # cat proc/kmsg > /storage/sdcard0/111.log
C:\Users\Administrator>adb pull /storage/sdcard0/111.log d:/111.log
2091 KB/s (128500 bytes in 0.060s)
系统日志就这么打出来了,
Failed to create client debugfs at /ion/clients/camera/jpeg-0
别说还真有一个关于驱动方面的错误,这个就不是我能解决的了,但是我想到了另一个方法把他绕过去上代码
takepic.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if(isVideoSnapshotSupported(mCamera.getParameters())){
try {
mCamera.takePicture(null, null, new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera arg1) {
try {
mCamera.reconnect();
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
Bitmap bit = BitmapFactory.decodeByteArray(data, 0,
data.length, options);
if (bit == null) {
Toast.makeText(MainActivity.this, "无法获取图片",
Toast.LENGTH_SHORT).show();
return;
}
iv_takepic.setImageBitmap(bit);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}else {
Toast.makeText(MainActivity.this, "不支持", Toast.LENGTH_SHORT).show();
}
}
也就是在拍完照的时候重新连接一下camera ,再重新预览 这个会有明显的卡顿,但是只要执行过一次录像功能就不会出现这种情况了,
说了这么多到此结束附上代码
MainActivity.java 代码
package com.example.tsmrecord;
import java.io.IOException;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity implements SurfaceTextureListener {
private Camera mCamera;
private TextureView mPreview;
private Button captureButton;
private Button takepic;
private ImageView iv_takepic;
private Options options;
private LmMediaRecord record;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
mPreview = (TextureView) findViewById(R.id.texture_content);
mPreview.setSurfaceTextureListener(this);
captureButton = (Button) findViewById(R.id.btn_cap);
captureButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if(record.isRecording()){
record.stopRecord();
captureButton.setText("录像");
}
else {
captureButton.setText("停止");
record.startRecord();
}
}
});
takepic = (Button) findViewById(R.id.btn_takepic);
iv_takepic = (ImageView) findViewById(R.id.iv_takepic);
options = new BitmapFactory.Options();
takepic.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if(isVideoSnapshotSupported(mCamera.getParameters())){
try {
mCamera.takePicture(null, null, new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera arg1) {
try {
mCamera.reconnect();
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
Bitmap bit = BitmapFactory.decodeByteArray(data, 0,
data.length, options);
if (bit == null) {
Toast.makeText(MainActivity.this, "无法获取图片",
Toast.LENGTH_SHORT).show();
return;
}
iv_takepic.setImageBitmap(bit);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}else {
Toast.makeText(MainActivity.this, "不支持", Toast.LENGTH_SHORT).show();
}
}
});
}
public static final String TRUE = "true";
private static final String VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported";
public static boolean isVideoSnapshotSupported(Parameters params) {
return TRUE.equals(params.get(VIDEO_SNAPSHOT_SUPPORTED));
}
private void startPreview(){
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
this.record=new LmMediaRecord(mCamera);
// We need to make sure that our preview and recording video size are
// supported by the
// camera. Query camera to find all the sizes and choose the optimal
// size given the
// dimensions of our preview surface.
// Camera.Parameters parameters = mCamera.getParameters();
// List<Camera.Size> mSupportedPreviewSizes = parameters
// .getSupportedPreviewSizes();
// Camera.Size optimalSize =mCamera.getParameters().getPreviewSize();
mPreview.getHeight());
// // Use the same size for recording profile.
// CamcorderProfile profile = CamcorderProfile
// .get(CamcorderProfile.QUALITY_HIGH);
// profile.videoFrameWidth = optimalSize.width;
// profile.videoFrameHeight = optimalSize.height;
// // likewise for the camera object itself.
// parameters.setPreviewSize(profile.videoFrameWidth,
// profile.videoFrameHeight);
// mCamera.setParameters(parameters);
try {
// Requires API level 11+, For backward compatibility use {@link
// setPreviewDisplay}
// with {@link SurfaceView}
mCamera.setPreviewTexture(mPreview.getSurfaceTexture());
mCamera.startPreview();
mCamera.lock();
} catch (IOException e) {
Log.e("info",
"Surface texture is unavailable or unsuitable"
+ e.getMessage());
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture arg0, int arg1,
int arg2) {
startPreview();
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) {
if (mCamera != null) {
// release the camera for other applications
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
return false;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,
int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture arg0) {
// TODO Auto-generated method stub
}
}
package com.example.tsmrecord;
import java.io.File;
import java.io.IOException;
import android.hardware.Camera;
import android.media.MediaRecorder;
import android.media.MediaRecorder.AudioEncoder;
import android.os.Environment;
public class LmMediaRecord {
private Camera mCamera;
private MediaRecorder mMediaRecorder;
private boolean isRecording;
public LmMediaRecord(Camera camera) {
this.mCamera = camera;
isRecording = false;
}
public void startRecord() {
mMediaRecorder = new MediaRecorder();
// Step 1: Unlock and set camera to MediaRecorder
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
// if (false) {
// try {
// mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// mMediaRecorder.setAudioEncoder(AudioEncoder.AAC);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
File file = new File(Environment.getExternalStorageDirectory()
+ File.separator + "tsmtexture" + File.separator
+ System.currentTimeMillis() + ".mp4");
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
mMediaRecorder.setOutputFile(file.getAbsolutePath());
// mMediaRecorder.setVideoSize(1920, 1080);
mMediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
mMediaRecorder.setOrientationHint(0);
try {
mMediaRecorder.prepare();
mMediaRecorder.start();
isRecording = true;
} catch (IllegalStateException e) {
} catch (IOException e) {
}
}
/**
* @return the isRecording
*/
public boolean isRecording() {
return isRecording;
}
public void stopRecord() {
if (mMediaRecorder != null) {
mMediaRecorder.stop();
// clear recorder configuration
mMediaRecorder.reset();
// release the recorder object
mMediaRecorder.release();
this.isRecording = false;
mMediaRecorder = null;
// Lock camera for later use i.e taking it back from MediaRecorder.
// MediaRecorder doesn't need it anymore and we will release it if
// the activity pauses.
mCamera.lock();
}
}
}
布局文件
<RelativeLayout 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">
<TextureView
android:id="@+id/texture_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_takepic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拍照"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"/>
<Button
android:id="@+id/btn_cap"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="录像"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"/>
<ImageView
android:id="@+id/iv_takepic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
权限随便从公司的项目里弄的,能用就好,没有仔细看
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" >
</uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 悬浮窗口权限 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- GPS权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 调节屏幕亮度权限 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- 2G3G信号权限 -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" >
</uses-permission>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
</uses-permission>
<uses-permission android:name="android.permission.RESTART_PACKAGES" >
</uses-permission>
<!-- <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
http://download.csdn.net/detail/qq_22256027/9492763 这是源码下载地址
http://download.csdn.net/detail/qq_22256027/9492761 opengl的解决方式,个人感觉比camera2的好,很简单
原文:https://blog.csdn.net/qq_22256027/article/details/51151872