gpt4 book ai didi

android - 如何在 Android 的 MediaCodec 上下文中使用 ByteBuffer

转载 作者:塔克拉玛干 更新时间:2023-11-01 21:43:11 25 4
gpt4 key购买 nike

到目前为止,我能够设置 MediaCodec 来对视频流进行编码。目的是将我的用户生成的作品保存到视频文件中。

我使用用户图片的 android 位图对象将帧推送到流中。

请参阅我在本文底部使用的代码 fragment (这是完整的代码,没有任何删减):

MediaCodec 使用 ByteBuffer 来处理视频/音频流。

位图基于 int[],如果转换为 byte[] 将需要 4 倍的 int[]

我做了一些研究来弄清楚在 MediaCodec 中处理视频/音频流时 ByteBuffer 有哪些契约(Contract),但信息几乎是零。

所以,MediaCodec 中的 ByteBuffer 使用契约是什么?

在 MediaFormat 中指定帧尺寸是否自动意味着 ByteBuffers 具有宽度 * 高度 * 4 字节的容量?

(我为每一帧一次使用一个位图对象)

感谢您的帮助。

(已编辑,已添加代码)

    import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;

import android.graphics.Rect;
import android.graphics.Bitmap.CompressFormat;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.CamcorderProfile;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.util.Log;
import android.view.View;

public class VideoCaptureManager {

private boolean running;

private long presentationTime;

public void start(View rootView, String saveFilePath){
Log.e("OUT", saveFilePath);
this.running = true;
this.presentationTime = 0;
this.capture(rootView, saveFilePath);
}

private void capture(final View rootView, String saveFilePath){
if(rootView != null){
rootView.setDrawingCacheEnabled(true);

final Rect drawingRect = new Rect();
rootView.getDrawingRect(drawingRect);

try{
final File file = new File(saveFilePath);
if(file.exists()){
// File exists return
return;
} else {
File parent = file.getParentFile();
if(!parent.exists()){
parent.mkdirs();
}
}

new Thread(){
public void run(){
try{
DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));

MediaCodec codec = MediaCodec.createEncoderByType("video/mp4v-es");
MediaFormat mediaFormat = null;
if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){
mediaFormat = MediaFormat.createVideoFormat("video/mp4v-es", 720, 1280);
} else {
mediaFormat = MediaFormat.createVideoFormat("video/mp4v-es", 480, 720);
}


mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 700000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 10);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
codec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

codec.start();

ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();

while(VideoCaptureManager.this.running){
try{
int inputBufferIndex = codec.dequeueInputBuffer(-2);
if(inputBufferIndex >= 0){
// Fill in the bitmap bytes
// inputBuffers[inputBufferIndex].
ByteArrayOutputStream baos = new ByteArrayOutputStream();
rootView.getDrawingCache().compress(CompressFormat.JPEG, 80, baos);
inputBuffers[inputBufferIndex].put(baos.toByteArray());

codec.queueInputBuffer(inputBufferIndex, 0, inputBuffers[inputBufferIndex].capacity(), presentationTime, MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
presentationTime += 100;
}

BufferInfo info = new BufferInfo();
int outputBufferIndex = codec.dequeueOutputBuffer(info, -2);
if(outputBufferIndex >= 0){
// Write the bytes to file
byte[] array = outputBuffers[outputBufferIndex].array(); // THIS THORWS AN EXCEPTION. WHAT IS THE CONTRACT TO DEAL WITH ByteBuffer in this code?
if(array != null){
dos.write(array);
}

codec.releaseOutputBuffer(outputBufferIndex, false);
} else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
outputBuffers = codec.getOutputBuffers();
} else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
// codec format is changed
MediaFormat format = codec.getOutputFormat();
}

Thread.sleep(100);
}catch(Throwable th){
Log.e("OUT", th.getMessage(), th);
}
}

codec.stop();
codec.release();
codec = null;

dos.flush();
dos.close();
}catch(Throwable th){
Log.e("OUT", th.getMessage(), th);
}
}
}.start();

}catch(Throwable th){
Log.e("OUT", th.getMessage(), th);
}
}
}

public void stop(){
this.running = false;
}
}

最佳答案

ByteBuffer 的确切布局由您选择的输入格式的编解码器决定。并非所有设备都支持所有可能的输入格式(例如,某些 AVC 编码器需要平面 420 YUV,其他需要半平面)。旧版本的 Android(<= API 17)并没有真正提供一种可移植的方式来为 MediaCodec 软件生成视频帧。

在 Android 4.3 (API 18) 中,您有两个选择。首先,MediaCodec 现在接受来自 Surface 的输入,这意味着您可以使用 OpenGL ES 绘制的任何内容都可以录制为电影。参见,例如,EncodeAndMuxTest sample .

其次,您仍然可以选择使用软件生成的 YUV 420 缓冲区,但现在它们更有可能工作,因为有 CTS 测试可以使用它们。您仍然需要对平面或半平面进行运行时检测,但实际上只有两种布局。查看 EncodeDecodeTest 的缓冲区到缓冲区变体举个例子。

关于android - 如何在 Android 的 MediaCodec 上下文中使用 ByteBuffer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14806987/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com