我正在尝试在我的三星 Galaxy S6(支持 1920x1080 约 30 fps)上录制来自后置摄像头(面向脸部的摄像头)的视频。如果我不需要,我不想使用任何表面进行预览,因为这只是在后台发生。
我似乎可以正常工作,但是输出文件无法以实际上正确的方式播放。在我的 Windows 10 PC 上,Windows Media Player 将显示第一帧然后播放音频,VLC 不会显示任何帧。在我的手机上,录制的文件可以播放,但不能完全播放。它将保持第一帧 5-8 秒,然后在最后,剩余时间变为 0,显示的总时间发生变化,然后开始播放实际的视频帧。在我的 Mac (10.9.5) 上,Quicktime 不会显示视频(虽然没有错误),但 Google Picasa 可以完美播放。我想在我的 PC 上试用 Picasa,看看它是否在那里工作,但我无法再下载 Google Picasa,因为它已经日落了。
我尝试为我找到的 Windows 安装编解码器包,但这并没有解决任何问题。MediaInfo v0.7.85 报告此文件:
一般的 全名:C:\...\1465655479915.mp4 格式:MPEG-4 格式配置文件:基础媒体/版本 2 编解码器 ID:mp42 (isom/mp42) 文件大小:32.2 MiB 持续时间:15s 744ms 总比特率:17.1 Mbps 编码日期:UTC 2016-06-11 14:31:50 标记日期:UTC 2016-06-11 14:31:50 com.android.version:6.0.1 视频 编号:1 格式:AVC 格式/信息:高级视频编解码器 格式配置文件:高@L4 格式设置,CABAC:是 格式设置,ReFrames : 1 帧 格式设置,GOP:M=1,N=30 编解码器 ID:avc1 编解码器 ID/信息:高级视频编码 持续时间:15s 627ms 比特率:16.2 Mbps 宽度:1 920 像素 高度:1 080 像素 显示纵横比:16:9 帧率模式:可变 帧率:0.000 (0/1000) fps 最低帧率:0.000 fps 最大帧率:30.540 fps 色彩空间:YUV 色度二次采样:4:2:0 位深:8位 扫描类型:逐行扫描 流大小:0.00 字节 (0%) 源流大小:31.7 MiB (98%) 标题:视频句柄 英语语言 编码日期:UTC 2016-06-11 14:31:50 标记日期:UTC 2016-06-11 14:31:50 mdhd_持续时间:15627 声音的 编号:2 格式:AAC 格式/信息:高级音频编解码器 格式配置文件:LC 编解码器 ID:40 持续时间:15s 744ms 比特率模式:恒定 比特率:256 Kbps 频道:2 个频道 通道位置:前:LR 采样率:48.0 KHz 帧率:46.875 fps (1024 spf) 压缩模式:有损 流大小:492 KiB (1%) 标题:声音手柄 英语语言 编码日期:UTC 2016-06-11 14:31:50 标记日期:UTC 2016-06-11 14:31:50
我用来创建它的代码是:
package invisiblevideorecorder;
import android.content.Context;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.Surface;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
/**
* @author Mark
* @since 6/10/2016
*/
public class InvisibleVideoRecorder {
private static final String TAG = "InvisibleVideoRecorder";
private final CameraCaptureSessionStateCallback cameraCaptureSessionStateCallback = new CameraCaptureSessionStateCallback();
private final CameraDeviceStateCallback cameraDeviceStateCallback = new CameraDeviceStateCallback();
private MediaRecorder mediaRecorder;
private CameraManager cameraManager;
private Context context;
private CameraDevice cameraDevice;
private HandlerThread handlerThread;
private Handler handler;
public InvisibleVideoRecorder(Context context) {
this.context = context;
handlerThread = new HandlerThread("camera");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
try {
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
final String filename = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES).getAbsolutePath() + File.separator + System.currentTimeMillis() + ".mp4";
mediaRecorder.setOutputFile(filename);
Log.d(TAG, "start: " + filename);
// by using the profile, I don't think I need to do any of these manually:
// mediaRecorder.setVideoEncodingBitRate(16000000);
// mediaRecorder.setVideoFrameRate(30);
// mediaRecorder.setCaptureRate(30);
// mediaRecorder.setVideoSize(1920, 1080);
// mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
// mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
// Log.d(TAG, "start: 1 " + CamcorderProfile.hasProfile(CameraMetadata.LENS_FACING_BACK, CamcorderProfile.QUALITY_1080P));
// true
// Log.d(TAG, "start: 2 " + CamcorderProfile.hasProfile(CameraMetadata.LENS_FACING_BACK, CamcorderProfile.QUALITY_HIGH_SPEED_1080P));
// false
// Log.d(TAG, "start: 3 " + CamcorderProfile.hasProfile(CameraMetadata.LENS_FACING_BACK, CamcorderProfile.QUALITY_HIGH));
// true
CamcorderProfile profile = CamcorderProfile.get(CameraMetadata.LENS_FACING_BACK, CamcorderProfile.QUALITY_1080P);
Log.d(TAG, "start: profile " + ToString.inspect(profile));
// start: 0 android.media.CamcorderProfile@114016694 {
// audioBitRate: 256000
// audioChannels: 2
// audioCodec: 3
// audioSampleRate: 48000
// duration: 30
// fileFormat: 2
// quality: 6
// videoBitRate: 17000000
// videoCodec: 2
// videoFrameHeight: 1080
// videoFrameRate: 30
// videoFrameWidth: 1920
// }
mediaRecorder.setOrientationHint(0);
mediaRecorder.setProfile(profile);
mediaRecorder.prepare();
} catch (IOException e) {
Log.d(TAG, "start: exception" + e.getMessage());
}
}
public void start() {
Log.d(TAG, "start: ");
cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
try {
cameraManager.openCamera(String.valueOf(CameraMetadata.LENS_FACING_BACK), cameraDeviceStateCallback, handler);
} catch (CameraAccessException | SecurityException e) {
Log.d(TAG, "start: exception " + e.getMessage());
}
}
public void stop() {
Log.d(TAG, "stop: ");
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
cameraDevice.close();
try {
handlerThread.join();
} catch (InterruptedException e) {
}
}
private class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback {
private final static String TAG = "CamCaptSessionStCb";
@Override
public void onActive(CameraCaptureSession session) {
Log.d(TAG, "onActive: ");
super.onActive(session);
}
@Override
public void onClosed(CameraCaptureSession session) {
Log.d(TAG, "onClosed: ");
super.onClosed(session);
}
@Override
public void onConfigured(CameraCaptureSession session) {
Log.d(TAG, "onConfigured: ");
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
Log.d(TAG, "onConfigureFailed: ");
}
@Override
public void onReady(CameraCaptureSession session) {
Log.d(TAG, "onReady: ");
super.onReady(session);
try {
CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
builder.addTarget(mediaRecorder.getSurface());
CaptureRequest request = builder.build();
session.setRepeatingRequest(request, null, handler);
mediaRecorder.start();
} catch (CameraAccessException e) {
Log.d(TAG, "onConfigured: " + e.getMessage());
}
}
@Override
public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
Log.d(TAG, "onSurfacePrepared: ");
super.onSurfacePrepared(session, surface);
}
}
private class CameraDeviceStateCallback extends CameraDevice.StateCallback {
private final static String TAG = "CamDeviceStateCb";
@Override
public void onClosed(CameraDevice camera) {
Log.d(TAG, "onClosed: ");
super.onClosed(camera);
}
@Override
public void onDisconnected(CameraDevice camera) {
Log.d(TAG, "onDisconnected: ");
}
@Override
public void onError(CameraDevice camera, int error) {
Log.d(TAG, "onError: ");
}
@Override
public void onOpened(CameraDevice camera) {
Log.d(TAG, "onOpened: ");
cameraDevice = camera;
try {
camera.createCaptureSession(Arrays.asList(mediaRecorder.getSurface()), cameraCaptureSessionStateCallback, handler);
} catch (CameraAccessException e) {
Log.d(TAG, "onOpened: " + e.getMessage());
}
}
}
}
我遵循了 Android 源代码(测试和应用程序)代码,以及我在 github 上找到的几个示例,以便弄清楚这一点,因为 camera2 API 的文档还不是很好。
有什么明显的我做错了吗?或者,我只是在 Mac 上缺少编解码器以供 Quicktime 使用,而在我的 PC 上缺少编解码器以供 Windows Media Player 和 VLC 使用?我还没有尝试在 Linux 上播放这些文件,所以我还不知道那里会发生什么。哦,如果我将 mp4 文件上传到 photos.google.com,它们也可以在那里完全正确地播放。
谢谢!标记