【发布时间】:2016-02-13 15:50:39
【问题描述】:
我正在尝试使用 javacv 在 vine 中以 480*480 分辨率录制视频。作为起点,我使用了https://github.com/bytedeco/javacv/blob/master/samples/RecordActivity.java 中提供的示例视频正在被录制(但不是以所需的分辨率)并保存。
但问题是 android 原生不支持 480*480 分辨率。因此,需要进行一些预处理才能获得所需分辨率的视频。
因此,一旦我能够使用 javacv 提供的代码示例录制视频,接下来的挑战就是如何预处理视频。研究发现,当所需的最终图像宽度与记录的图像宽度相同时,可以进行有效裁剪。 SO问题Recording video on Android using JavaCV (Updated 2014 02 17)中提供了这样的解决方案。我按照该答案中的建议更改了 onPreviewFrame 方法。
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
startTime = System.currentTimeMillis();
return;
}
if (RECORD_LENGTH > 0) {
int i = imagesIndex++ % images.length;
yuvImage = images[i];
timestamps[i] = 1000 * (System.currentTimeMillis() - startTime);
}
/* get video data */
imageWidth = 640;
imageHeight = 480
int finalImageHeight = 360;
if (yuvImage != null && recording) {
ByteBuffer bb = (ByteBuffer)yuvImage.image[0].position(0); // resets the buffer
final int startY = imageWidth*(imageHeight-finalImageHeight)/2;
final int lenY = imageWidth*finalImageHeight;
bb.put(data, startY, lenY);
final int startVU = imageWidth*imageHeight + imageWidth*(imageHeight-finalImageHeight)/4;
final int lenVU = imageWidth* finalImageHeight/2;
bb.put(data, startVU, lenVU);
try {
long t = 1000 * (System.currentTimeMillis() - startTime);
if (t > recorder.getTimestamp()) {
recorder.setTimestamp(t);
}
recorder.record(yuvImage);
} catch (FFmpegFrameRecorder.Exception e) {
Log.e(LOG_TAG, "problem with recorder():", e);
}
}
}
}
还请注意,此解决方案是为较旧版本的 javacv 提供的。生成的视频有覆盖 2/3 部分的黄色覆盖层。由于视频未正确裁剪,左侧也有空白部分。
所以我的问题是使用最新版本的 javacv 裁剪视频最合适的解决方案是什么?
按照 Alex Cohn 的建议进行更改后的代码
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
startTime = System.currentTimeMillis();
return;
}
if (RECORD_LENGTH > 0) {
int i = imagesIndex++ % images.length;
yuvImage = images[i];
timestamps[i] = 1000 * (System.currentTimeMillis() - startTime);
}
/* get video data */
imageWidth = 640;
imageHeight = 480;
destWidth = 480;
if (yuvImage != null && recording) {
ByteBuffer bb = (ByteBuffer)yuvImage.image[0].position(0); // resets the buffer
int start = 2*((imageWidth-destWidth)/4); // this must be even
for (int row=0; row<imageHeight*3/2; row++) {
bb.put(data, start, destWidth);
start += imageWidth;
}
try {
long t = 1000 * (System.currentTimeMillis() - startTime);
if (t > recorder.getTimestamp()) {
recorder.setTimestamp(t);
}
recorder.record(yuvImage);
} catch (FFmpegFrameRecorder.Exception e) {
Log.e(LOG_TAG, "problem with recorder():", e);
}
}
}
使用此代码(destWidth 480)生成的视频的屏幕截图是
接下来我尝试使用指定为 639 的 destWidth 捕获视频。结果是
当 destWidth 为 639 时,视频重复内容两次。 480时,内容重复5次,绿色叠加和失真更多。
此外,当 destWidth = imageWidth 时,可以正确捕获视频。即,对于 640*480,视频内容没有重复,也没有绿色覆盖。
将帧转换为 IplImage
当第一次问这个问题时,我没有提到 FFmpegFrameRecorder 中的记录方法现在接受 Frame 类型的对象,而之前它是 IplImage 对象。所以我尝试通过将 Frame 转换为 IplImage 来应用 Alex Cohn 的解决方案。
//---------------------------------------
// initialize ffmpeg_recorder
//---------------------------------------
private void initRecorder() {
Log.w(LOG_TAG,"init recorder");
imageWidth = 640;
imageHeight = 480;
if (RECORD_LENGTH > 0) {
imagesIndex = 0;
images = new Frame[RECORD_LENGTH * frameRate];
timestamps = new long[images.length];
for (int i = 0; i < images.length; i++) {
images[i] = new Frame(imageWidth, imageHeight, Frame.DEPTH_UBYTE, 2);
timestamps[i] = -1;
}
} else if (yuvImage == null) {
yuvImage = new Frame(imageWidth, imageHeight, Frame.DEPTH_UBYTE, 2);
Log.i(LOG_TAG, "create yuvImage");
OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
yuvIplimage = converter.convert(yuvImage);
}
Log.i(LOG_TAG, "ffmpeg_url: " + ffmpeg_link);
recorder = new FFmpegFrameRecorder(ffmpeg_link, imageWidth, imageHeight, 1);
recorder.setFormat("flv");
recorder.setSampleRate(sampleAudioRateInHz);
// Set in the surface changed method
recorder.setFrameRate(frameRate);
Log.i(LOG_TAG, "recorder initialize success");
audioRecordRunnable = new AudioRecordRunnable();
audioThread = new Thread(audioRecordRunnable);
runAudioThread = true;
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
startTime = System.currentTimeMillis();
return;
}
if (RECORD_LENGTH > 0) {
int i = imagesIndex++ % images.length;
yuvImage = images[i];
timestamps[i] = 1000 * (System.currentTimeMillis() - startTime);
}
/* get video data */
int destWidth = 640;
if (yuvIplimage != null && recording) {
ByteBuffer bb = yuvIplimage.getByteBuffer(); // resets the buffer
int start = 2*((imageWidth-destWidth)/4); // this must be even
for (int row=0; row<imageHeight*3/2; row++) {
bb.put(data, start, destWidth);
start += imageWidth;
}
try {
long t = 1000 * (System.currentTimeMillis() - startTime);
if (t > recorder.getTimestamp()) {
recorder.setTimestamp(t);
}
recorder.record(yuvImage);
} catch (FFmpegFrameRecorder.Exception e) {
Log.e(LOG_TAG, "problem with recorder():", e);
}
}
}
但是用这种方法生成的视频只包含绿框。
【问题讨论】:
标签: android opencv video ffmpeg javacv