本讲是android camera专题系列的第29讲,我们介绍android camera2 api专题的slowmotion实战。
更多资源:
资源 | 描述 |
---|---|
在线课程 | |
知识星球 | 星球名称:深入浅出android camera 星球id: 17296815 |
极客笔记圈 |
判断是否支持slow motion
camera方面
capability
- 是否支持request_available_capabilities_constrained_high_speed_video
streamconfigurationmap
- gethighspeedvideosizes()不为null
-
每一种size对应的gethighspeedvideofpsrangesfor不为null
实战代码
geekcamera2\app\src\main\java\com\deepinout\geekcamera\cts\helpers\staticmetadata.java#ishighspeedvideosupported
public boolean ishighspeedvideosupported() {
if (!iscapabilitysupported(
cameracharacteristics.request_available_capabilities_constrained_high_speed_video)) {
return false;
}
streamconfigurationmap config =
getvaluefromkeynonnull(cameracharacteristics.scaler_stream_configuration_map);
if (config == null) {
return false;
}
size[] availablesizes = config.gethighspeedvideosizes();
if (availablesizes.length == 0) {
return false;
}
for (size size : availablesizes) {
range[] availablefpsranges = config.gethighspeedvideofpsrangesfor(size);
if (availablefpsranges.length == 0) {
return false;
}
}
return true;
}
video encoder方面
从camcorderprofile判断是否支持high speed quality的profile
实战代码
geekcamera2\app\src\main\java\com\deepinout\geekcamera\cameracontroller\cameracontroller2.java#getfpsfromhighspeedprofileforsize
private int getfpsfromhighspeedprofileforsize(android.util.size size) {
for (int quality = camcorderprofile.quality_high_speed_low;
quality <= camcorderprofile.quality_high_speed_2160p; quality ) {
if (camcorderprofile.hasprofile(quality)) {
camcorderprofile profile = camcorderprofile.get(quality);
if (size.equals(new android.util.size(profile.videoframewidth, profile.videoframeheight))){
return profile.videoframerate;
}
}
}
return 0;
}
slow motion支持的size和fps
遍历camera支持的size和fps,对每一种size和fps的组合去检查camcorderprofile是否支持
实战代码
geekcamera2\app\src\main\java\com\deepinout\geekcamera\cameracontroller\cameracontroller2.java#doinitsupportedhighspeedvideosizes
private void doinitsupportedhighspeedvideosizes(camerafeatures camera_features,
streamconfigurationmap configs) {
log.i(tag, "[slow_motion] doinitsupportedhighspeedvideosizes");
hs_fps_ranges = new arraylist<>();
camera_features.msupportedvideosizeshighspeed = new arraylist<>();
for (range r : configs.gethighspeedvideofpsranges()) {
hs_fps_ranges.add(new int[] {r.getlower(), r.getupper()});
}
collections.sort(hs_fps_ranges, new cameracontroller.rangesorter());
if( mydebug.log ) {
log.i(tag, "[slow_motion] camera supported high speed video fps ranges: ");
for (int[] f : hs_fps_ranges) {
log.i(tag, "[slow_motion] camera hs range: [" f[0] "-" f[1] "]");
}
log.i(tag, "[slow_motion] video supported high speed video camcorder profiles: ");
for (int quality = camcorderprofile.quality_high_speed_low;
quality <= camcorderprofile.quality_high_speed_2160p; quality ) {
if (camcorderprofile.hasprofile(quality)) {
camcorderprofile profile = camcorderprofile.get(quality);
log.i(tag, "[slow_motion] video hs camcorder profile:" profile.videoframewidth
"x" profile.videoframeheight "@" profile.videoframerate " fps"
",quality:" quality);
}
}
}
android.util.size[] camera_video_sizes_high_speed = configs.gethighspeedvideosizes();
for(android.util.size camera_size : camera_video_sizes_high_speed) {
for (range r : configs.gethighspeedvideofpsrangesfor(camera_size)) {
int profile_fps = getfpsfromhighspeedprofileforsize(camera_size);
if (r.getupper() > r.getlower()) {
log.w(tag, "[slow_motion] skip " camera_size "@fps range:" r.tostring());
continue;
}
if (r.getupper() != profile_fps) {
log.w(tag, "[slow_motion] high speed recording " camera_size "@" r.getupper() "fps"
" is not supported by camcorderprofile");
continue;
}
arraylist fr = new arraylist<>();
fr.add(new int[] { r.getlower(), r.getupper()});
cameracontroller.size hs_video_size = new cameracontroller.size(
camera_size.getwidth(),
camera_size.getheight(),
fr,
true);
if (mydebug.log) {
log.i(tag, "[slow_motion] added high speed video size: "
hs_video_size
", fps range:" r.tostring());
}
camera_features.msupportedvideosizeshighspeed.add(hs_video_size);
}
}
collections.sort(camera_features.msupportedvideosizeshighspeed, new cameracontroller.sizesorter());
}
camera流程控制
session创建
- 创建sessionconfiguration时,session type要设置为sessionconfiguration.session_high_speed
-
只配置一个preview surface 一个video recording surface,不支持video snapshot
实战代码
geekcamera2\app\src\main\java\com\deepinout\geekcamera\cameracontroller\cameracontroller2.java#createcapturesession
msessionconfiguration = new sessionconfiguration(
is_video_high_speed ?
sessionconfiguration.session_high_speed : sessionconfiguration.session_regular,
outputconfigurations,
new cameratestutils.handlerexecutor(mcamerabackgroundhandler),
mystatecallback
);
repeating burst request创建
- 调用cameraconstrainedhighspeedcapturesession# createhighspeedrequestlist,根据一个request产生一组capturerequest
实战代码
geekcamera2\app\src\main\java\com\deepinout\geekcamera\cameracontroller\cameracontroller2.java#setrepeatingrequest
if( is_video_high_speed && build.version.sdk_int >= build.version_codes.m ) {
cameraconstrainedhighspeedcapturesession capturesessionhighspeed = (cameraconstrainedhighspeedcapturesession) mcameracapturesession;
list mpreviewbuilderburst = capturesessionhighspeed.createhighspeedrequestlist(request);
log.i(tag, "[slow_motion] setrepeatingburst createhighspeedrequestlist size:" mpreviewbuilderburst.size());
capturesessionhighspeed.setrepeatingburst(mpreviewbuilderburst, previewcapturecallback, mcamerabackgroundhandler);
}
repeating burst capturerequest的个数(batch size)是如何决定决定的
- 录像过程中预览一般30fps就足够了,因此batch size的计算会想办法将预览帧率限制在30fps
-
举例240fps的capturerequest list有8个batch size
mediarecorder流程控制
将camcorderprofile中的值设置给mediarecorder
setvideoframerate
- slow motion
- 建议不要录制audio
-
通过setvideoframerate设置的video frame rate要小于capture rate,建议为30
-
高帧率视频
- video frame rate与capture rate相等
实战代码
geekcamera2\app\src\main\java\com\deepinout\geekcamera\preview\videoprofile.java#copytomediarecorder
public void copytomediarecorder(mediarecorder media_recorder, boolean slow_motion) {
if( mydebug.log )
log.d(tag, "copytomediarecorder: " media_recorder tostring());
if( record_audio && !slow_motion) {
if( mydebug.log )
log.d(tag, "record audio");
media_recorder.setaudiosource(this.audiosource);
}
media_recorder.setvideosource(this.videosource);
// n.b., order may be important - output format should be first, at least
// also match order of mediarecorder.setprofile() just to be safe, see https://stackoverflow.com/questions/5524672/is-it-possible-to-use-camcorderprofile-without-audio-source
media_recorder.setoutputformat(this.fileformat);
if (slow_motion) {
media_recorder.setvideoframerate(30);
} else {
media_recorder.setvideoframerate(this.videoframerate);
}
media_recorder.setcapturerate(this.videocapturerate);
media_recorder.setvideosize(this.videoframewidth, this.videoframeheight);
media_recorder.setvideoencodingbitrate(this.videobitrate);
media_recorder.setvideoencoder(this.videocodec);
if( record_audio && !slow_motion) {
media_recorder.setaudioencodingbitrate(this.audiobitrate);
media_recorder.setaudiochannels(this.audiochannels);
media_recorder.setaudiosamplingrate(this.audiosamplerate);
media_recorder.setaudioencoder(this.audiocodec);
}
if( mydebug.log )
log.d(tag, "done: " media_recorder);
}