我需要将 FFMPEG“原始”数据传回我的 JAVA 代码,以便在屏幕上显示它。 我有一个处理 FFMPEG 的 native 方法,然后在 java 中调用一个方法,该方法将 Byte[](到目前为止)作为参数。
传递的字节数组由 JAVA 读取,但在执行 BitmapFactory.decodeByteArray(bitmap, 0, bitmap.length);
时返回 null。我已经打印出数组并得到 200k 个元素(这是预期的),但无法解码。到目前为止,我正在做的是从 AvFrame->data
中获取数据并将其转换为 unsigned char *
,然后将其转换为 jbyterArray
。在所有转换之后,我将 jbyteArray
作为参数传递给我的 JAVA 方法。我在这里缺少什么吗?为什么 BitmapFactory
不将数组解码为图像进行显示?
编辑 1.0
目前我正在尝试通过
获取我的图像public void setImage(ByteBuffer bmp) {
bmp.rewind();
Bitmap bitmap = Bitmap.createBitmap(1920, 1080, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(bmp);
runOnUiThread(() -> {
ImageView imgViewer = findViewById(R.id.mSurfaceView);
imgViewer.setImageBitmap(bitmap);
});
}
但我总是遇到异常
JNI DETECTED ERROR IN APPLICATION: JNI NewDirectByteBuffer called with pending exception java.lang.RuntimeException: Buffer not large enough for pixels
at void android.graphics.Bitmap.copyPixelsFromBuffer(java.nio.Buffer) (Bitmap.java:657)
at void com.example.asmcpp.MainActivity.setSurfaceImage(java.nio.ByteBuffer)
编辑1.1
所以,这是每次有帧传入时执行的完整代码。请注意,ByteBuffer 是从该方法中创建和传递的
void VideoClientInterface::onEncodedFrame(video::encoded_frame_t &encodedFrame) {
AVFrame *filt_frame = av_frame_alloc();
auto frame = std::shared_ptr<video::encoded_frame_t>(new video::encoded_frame_t,
[](video::encoded_frame_t *p) { if (p) delete p; });
if (frame) {
frame->size = encodedFrame.size;
frame->ssrc = encodedFrame.ssrc;
frame->width = encodedFrame.width;
frame->height = encodedFrame.height;
frame->dataType = encodedFrame.dataType;
frame->timestamp = encodedFrame.timestamp;
frame->frameIndex = encodedFrame.frameIndex;
frame->isKeyFrame = encodedFrame.isKeyFrame;
frame->isDroppable = encodedFrame.isDroppable;
frame->data = new char[frame->size];
if (frame->data) {
memcpy(frame->data, encodedFrame.data, frame->size);
AVPacket packet;
av_init_packet(&packet);
packet.dts = AV_NOPTS_VALUE;
packet.pts = encodedFrame.timestamp;
packet.data = (uint8_t *) encodedFrame.data;
packet.size = encodedFrame.size;
int ret = avcodec_send_packet(m_avCodecContext, &packet);
if (ret == 0) {
ret = avcodec_receive_frame(m_avCodecContext, m_avFrame);
if (ret == 0) {
m_transform = sws_getCachedContext(
m_transform, // previous context ptr
m_avFrame->width, m_avFrame->height, AV_PIX_FMT_YUV420P, // src
m_avFrame->width, m_avFrame->height, AV_PIX_FMT_RGB24, // dst
SWS_BILINEAR, nullptr, nullptr, nullptr // options
);
auto decodedFrame = std::make_shared<video::decoded_frame_t>();
decodedFrame->width = m_avFrame->width;
decodedFrame->height = m_avFrame->height;
decodedFrame->size = m_avFrame->width * m_avFrame->height * 3;
decodedFrame->timeStamp = m_avFrame->pts;
decodedFrame->data = new unsigned char[decodedFrame->size];
if (decodedFrame->data) {
uint8_t *dstSlice[] = {decodedFrame->data,
0,
0};// outFrame.bits(), outFrame.bits(), outFrame.bits()
const int dstStride[] = {decodedFrame->width * 3, 0, 0};
sws_scale(m_transform, m_avFrame->data, m_avFrame->linesize,
0, m_avFrame->height, dstSlice, dstStride);
auto m_rawData = decodedFrame->data;
auto len = strlen(reinterpret_cast<char *>(m_rawData));
if (frameCounter == 10) {
jobject newArray = GetJniEnv()->NewDirectByteBuffer(m_rawData, len);
GetJniEnv()->CallVoidMethod(m_obj, setSurfaceImage, newArray);
frameCounter = 0;
}
frameCounter++;
}
} else {
av_packet_unref(&packet);
}
} else {
av_packet_unref(&packet);
}
}
}
}
我什至不确定我是否正确地完成了那部分。如果您发现其中有任何错误,请随时指出。
最佳答案
您不能将 native 字节数组转换为 jbyteArray
并期望它能正常工作。 byte[]
是一个具有 length
字段、引用计数等的实际对象。
使用NewDirectByteBuffer
而不是将字节缓冲区包装到 Java ByteBuffer
,从那里您可以使用 .array()
获取实际的 byte[]
。
请注意,此 JNI 操作相对昂贵,因此如果您希望在每帧的基础上执行此操作,您可能需要预先分配一些字节缓冲区并告诉 FFmpeg 直接写入这些缓冲区。
关于android - 将 FFMPEG AvFrame 数据从 C++ 传递到 JAVA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58914293/