?相機幀捕獲
Camera操作類,包括相機預(yù)覽、錄像、拍照等功能接口。
接口名 |
描述 |
---|---|
triggerSingleCapture?(FrameConfig frameConfig) |
啟動相機幀的單幀捕獲。 |
triggerMultiCapture?(List<FrameConfig> frameConfigs) |
啟動相機幀的多幀捕獲。 |
configure?(CameraConfig config) |
配置相機。 |
flushCaptures?() |
停止并清除相機幀的捕獲,包括循環(huán)幀/單幀/多幀捕獲。 |
getCameraConfigBuilder?() |
獲取相機配置構(gòu)造器對象。 |
getCameraId?() |
獲取當(dāng)前相機的ID。 |
getFrameConfigBuilder?(int type) |
獲取指定類型的相機幀配置構(gòu)造器對象。 |
release?() |
釋放相機對象及資源。 |
triggerLoopingCapture?(FrameConfig frameConfig) |
啟動或者更新相機幀的循環(huán)捕獲。 |
stopLoopingCapture?() |
停止當(dāng)前相機幀的循環(huán)捕獲。 |
啟動預(yù)覽(循環(huán)幀捕獲)
用戶一般都是先看見預(yù)覽畫面才執(zhí)行拍照或者其他功能,所以對于一個普通的相機應(yīng)用,預(yù)覽是必不可少的。啟動預(yù)覽的建議步驟如下:
1. 通過getFrameConfigBuilder(FRAME_CONFIG_PREVIEW)方法獲取預(yù)覽配置模板,常用幀配置項見下表,更多的幀配置項以及詳細使用方法請參考API接口說明的FrameConfig.Builder部分。
接口名 |
描述 |
是否必選 |
---|---|---|
addSurface(Surface surface) |
配置預(yù)覽surface和幀的綁定。 |
是 |
setAfMode(int afMode, Rect rect) |
配置對焦模式。 |
否 |
setAeMode(int aeMode, Rect rect) |
配置曝光模式。 |
否 |
setZoom(float value) |
配置變焦值。 |
否 |
setFlashMode(int flashMode) |
配置閃光燈模式。 |
否 |
setFaceDetection(int type, boolean isEnable) |
配置人臉檢測或者笑臉檢測。 |
否 |
setParameter(Key<T> key, T value) |
配置其他屬性(如自拍鏡像等)。 |
否 |
setMark(Object mark) |
配置一個標(biāo)簽,后續(xù)可以從FrameConfig中通過Object getMark()拿到標(biāo)簽,判斷兩個是否相等,相等就說明是同一個配置。 |
否 |
setCoordinateSurface(Surface surface) |
配置坐標(biāo)系基準(zhǔn)Surface,后續(xù)計算Ae/Af等區(qū)域都會基于此Surface為基本的中心坐標(biāo)系,不設(shè)置默認使用添加的第一個Surface。 |
否 |
2. 通過triggerLoopingCapture(FrameConfig)方法實現(xiàn)循環(huán)幀捕獲(如預(yù)覽/錄像)。
private final class CameraStateCallbackImpl extends CameraStateCallback {
@Override
public void onConfigured(Camera camera) {
// 獲取預(yù)覽配置模板
frameConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW);
// 配置預(yù)覽Surface
frameConfigBuilder.addSurface(previewSurface);
previewFrameConfig = frameConfigBuilder.build();
try {
// 啟動循環(huán)幀捕獲
int triggerId = camera.triggerLoopingCapture(previewFrameConfig);
} catch (IllegalArgumentException e) {
HiLog.error(LABEL, "Argument Exception");
} catch (IllegalStateException e) {
HiLog.error(LABEL, "State Exception");
}
}
}
經(jīng)過以上的操作,相機應(yīng)用已經(jīng)可以正常進行實時預(yù)覽了。在預(yù)覽狀態(tài)下,開發(fā)者還可以執(zhí)行其他操作,比如:
當(dāng)預(yù)覽幀配置更改時,可以通過triggerLoopingCapture(FrameConfig)方法實現(xiàn)預(yù)覽幀配置的更新;
// 預(yù)覽幀變焦值變更
frameConfigBuilder.setZoom(1.2f);
// 調(diào)用triggerLoopingCapture方法實現(xiàn)預(yù)覽幀配置更新
triggerLoopingCapture(frameConfigBuilder.build());
通過stopLoopingCapture()方法停止循環(huán)幀捕獲(停止預(yù)覽)。
// 停止預(yù)覽幀捕獲
camera.stopLoopingCapture()
實現(xiàn)拍照(單幀捕獲)
拍照功能屬于相機應(yīng)用的最重要功能之一,而且照片質(zhì)量對用戶至關(guān)重要。相機模塊基于相機復(fù)雜的邏輯,從應(yīng)用接口層到器件驅(qū)動層都已經(jīng)默認的做好了最適合用戶的配置,這些默認配置盡可能地保證用戶拍出的每張照片的質(zhì)量。發(fā)起拍照的建議步驟如下:
1. 通過getFrameConfigBuilder(FRAME_CONFIG_PICTURE)方法獲取拍照配置模板,并且設(shè)置拍照幀配置,如下表
接口名 |
描述 |
是否必選 |
---|---|---|
FrameConfig.Builder addSurface(Surface) |
實現(xiàn)拍照Surface和幀的綁定。 |
必選 |
FrameConfig.Builder setImageRotation(int) |
設(shè)置圖片旋轉(zhuǎn)角度。 |
可選 |
FrameConfig.Builder setLocation(Location) |
設(shè)置圖片地理位置信息。 |
可選 |
FrameConfig.Builder setParameter(Key<T>, T) |
配置其他屬性(如自拍鏡像等)。 |
可選 |
2.?拍照前準(zhǔn)備圖像幀數(shù)據(jù)的接收實現(xiàn)
// 圖像幀數(shù)據(jù)接收處理對象
private ImageReceiver imageReceiver;
// 執(zhí)行回調(diào)的EventHandler
private EventHandler eventHandler = new EventHandler(EventRunner.create("CameraCb"));
// 拍照支持分辨率
private Size pictureSize;
// 單幀捕獲生成圖像回調(diào)Listener
private final ImageReceiver.IImageArrivalListener imageArrivalListener = new ImageReceiver.IImageArrivalListener() {
@Override
public void onImageArrival(ImageReceiver imageReceiver) {
StringBuffer fileName = new StringBuffer("picture_");
fileName.append(UUID.randomUUID()).append(".jpg"); // 定義生成圖片文件名
File myFile = new File(dirFile, fileName.toString()); // 創(chuàng)建圖片文件
imageSaver = new ImageSaver(imageReceiver.readNextImage(), myFile); // 創(chuàng)建一個讀寫線程任務(wù)用于保存圖片
eventHandler.postTask(imageSaver); // 執(zhí)行讀寫線程任務(wù)生成圖片
}
};
// 保存圖片, 圖片數(shù)據(jù)讀寫,及圖像生成見run方法
class ImageSaver implements Runnable {
private final Image myImage;
private final File myFile;
ImageSaver(Image image, File file) {
myImage = image;
myFile = file;
}
@Override
public void run() {
Image.Component component = myImage.getComponent(ImageFormat.ComponentType.JPEG);
byte[] bytes = new byte[component.remaining()];
component.read(bytes);
FileOutputStream output = null;
try {
output = new FileOutputStream(myFile);
output.write(bytes); // 寫圖像數(shù)據(jù)
} catch (IOException e) {
HiLog.error(LABEL, "save picture occur exception!");
} finally {
if (output != null) {
try {
output.close(); // 關(guān)閉流
} catch (IOException e) {
HiLog.error(LABEL, "image release occur exception!");
}
}
myImage.release();
}
}
}
private void takePictureInit() {
List<Size> pictureSizes = cameraAbility.getSupportedSizes(ImageFormat.JPEG); // 獲取拍照支持分辨率列表
pictureSize = getPictureSize(pictureSizes) // 根據(jù)拍照要求選擇合適的分辨率
imageReceiver = ImageReceiver.create(Math.max(pictureSize.width, pictureSize.height),
Math.min(pictureSize.width, pictureSize.height), ImageFormat.JPEG, 5); // 創(chuàng)建ImageReceiver對象,注意create函數(shù)中寬度要大于高度;5為最大支持的圖像數(shù),請根據(jù)實際設(shè)置。
imageReceiver.setImageArrivalListener(imageArrivalListener);
}
3.?通過triggerSingleCapture(FrameConfig)方法實現(xiàn)單幀捕獲(如拍照)。
private void capture() {
// 獲取拍照配置模板
framePictureConfigBuilder = cameraDevice.getFrameConfigBuilder(FRAME_CONFIG_PICTURE);
// 配置拍照Surface
framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
// 配置拍照其他參數(shù)
framePictureConfigBuilder.setImageRotation(90);
try {
// 啟動單幀捕獲(拍照)
cameraDevice.triggerSingleCapture(framePictureConfigBuilder.build());
} catch (IllegalArgumentException e) {
HiLog.error(LABEL, "Argument Exception");
} catch (IllegalStateException e) {
HiLog.error(LABEL, "State Exception");
}
}
為了捕獲到質(zhì)量更高和效果更好的圖片,還可以在幀結(jié)果中實時監(jiān)測自動對焦和自動曝光的狀態(tài),一般而言,在自動對焦完成,自動曝光收斂后的瞬間是發(fā)起單幀捕獲的最佳時機。
實現(xiàn)連拍(多幀捕獲)
連拍功能方便用戶一次拍照獲取多張照片,用于捕捉精彩瞬間。同普通拍照的實現(xiàn)流程一致,但連拍需要使用triggerMultiCapture(List<FrameConfig> frameConfigs)方法。
啟動錄像(循環(huán)幀捕獲)
啟動錄像和啟動預(yù)覽類似,但需要另外配置錄像Surface才能使用。
1.?錄像前需要進行音視頻模塊的配置。
private Source source; // 音視頻源
private AudioProperty.Builder audioPropertyBuilder; // 音頻屬性構(gòu)造器
private VideoProperty.Builder videoPropertyBuilder; // 視頻屬性構(gòu)造器
private StorageProperty.Builder storagePropertyBuilder; // 音視頻存儲屬性構(gòu)造器
private Recorder mediaRecorder; // 錄像操作對象
private String recordName; // 音視頻文件名
private Size mRecordSize; // 錄像分辨率
private void initMediaRecorder() {
videoPropertyBuilder.setRecorderBitRate(10000000); // 設(shè)置錄制比特率
int rotation = DisplayManager.getInstance().getDefaultDisplay(this).get().getRotation();
videoPropertyBuilder.setRecorderDegrees(getOrientation(rotation)); // 設(shè)置錄像方向
videoPropertyBuilder.setRecorderFps(30); // 設(shè)置錄制采樣率
videoPropertyBuilder.setRecorderHeight(Math.min(recordSize.height, recordSize.width)); // 設(shè)置錄像支持的分辨率,需保證width > height
videoPropertyBuilder.setRecorderWidth(Math.max(recordSize.height, recordSize.width));
videoPropertyBuilder.setRecorderVideoEncoder(Recorder.VideoEncoder.H264); // 設(shè)置視頻編碼方式
videoPropertyBuilder.setRecorderRate(30); // 設(shè)置錄制幀率
source.setRecorderAudioSource(Recorder.AudioSource.MIC); // 設(shè)置錄制音頻源
source.setRecorderVideoSource(Recorder.VideoSource.SURFACE); // 設(shè)置視頻窗口
mediaRecorder.setSource(source); // 設(shè)置音視頻源
mediaRecorder.setOutputFormat(Recorder.OutputFormat.MPEG_4); // 設(shè)置音視頻輸出格式
StringBuffer fileName = new StringBuffer("record_"); // 生成隨機文件名
fileName.append(UUID.randomUUID()).append(".mp4");
recordName = fileName.toString();
File file = new File(dirFile, recordName); // 創(chuàng)建錄像文件對象
storagePropertyBuilder.setRecorderFile(file); // 設(shè)置存儲音視頻文件名
mediaRecorder.setStorageProperty(storagePropertyBuilder.build());
audioPropertyBuilder.setRecorderAudioEncoder(Recorder.AudioEncoder.AAC); // 設(shè)置音頻編碼格式
mediaRecorder.setAudioProperty(audioPropertyBuilder.build()); // 設(shè)置音頻屬性
mediaRecorder.setVideoProperty(videoPropertyBuilder.build()); // 設(shè)置視頻屬性
mediaRecorder.prepare(); // 準(zhǔn)備錄制
HiLog.info(LABEL, "initMediaRecorder end");
}
2.?配置錄像幀,啟動錄像
private final class CameraStateCallbackImpl extends CameraStateCallback {
@Override
public void onConfigured(Camera camera) {
// 獲取錄像配置模板
frameConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_RECORD);
// 配置預(yù)覽Surface
frameConfigBuilder.addSurface(previewSurface);
// 配置錄像的Surface
mRecorderSurface = mediaRecorder.getVideoSurface();
frameConfigBuilder.addSurface(mRecorderSurface);
previewFrameConfig = frameConfigBuilder.build();
try {
// 啟動循環(huán)幀捕獲
int triggerId = camera.triggerLoopingCapture(previewFrameConfig);
} catch (IllegalArgumentException e) {
HiLog.error(LABEL, "Argument Exception");
} catch (IllegalStateException e) {
HiLog.error(LABEL, "State Exception");
}
}
}
相機設(shè)備釋放
使用完相機后,必須通過release()來關(guān)閉相機和釋放資源,否則可能導(dǎo)致其他相機應(yīng)用無法啟動。一旦相機被釋放,它所提供的操作就不能再被調(diào)用,否則會導(dǎo)致不可預(yù)期的結(jié)果,或是會引發(fā)狀態(tài)異常。文章來源:http://www.zghlxwxcb.cn/news/detail-506909.html
相機設(shè)備釋放的示例代碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-506909.html
private void releaseCamera() {
if (camera != null) {
// 關(guān)閉相機和釋放資源
camera.release();
camera = null;
}
// 拍照配置模板置空
framePictureConfigBuilder = null;
// 預(yù)覽配置模板置空
previewFrameConfig = null;
}
到了這里,關(guān)于HarmonyOS學(xué)習(xí)路之開發(fā)篇—多媒體開發(fā)(相機開發(fā) 二)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!