android FFmpeg 获取 视频帧 转 Bitmap
流程可以分为:
1、传入视频文件路径,解封装
2、找到视频流,从流中找到解码器
3、打开解码器,读取第一个完整的AVFrame帧
4、创建bitmap,使用libyuv将yuv转为argb关联给bitmap显示
5、释放资源
获取视频帧
//封装格式上下文
AVFormatContext *ifmt_ctx = NULL;
//打开输入源
ret = avformat_open_input(&ifmt_ctx, _path, 0, 0);
if (ret < 0) {
logDebug("解封装失败 -- %s", av_err2str(ret));
return nullptr;
}
ret = avformat_find_stream_info(ifmt_ctx, 0);
int video_stream_index = -1;
AVStream *pStream = NULL;
AVCodecParameters *codecpar = NULL;
//找出视频流
for (int i = 0; i < ifmt_ctx->nb_streams; ++i) {
pStream = ifmt_ctx->streams[i];
if (pStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
codecpar = pStream->codecpar;
video_stream_index = i;
}
}
//拷贝parameters 到 编解码器的context
ret = avcodec_parameters_to_context(codec_ctx, codecpar);
//打开编解码器
ret = avcodec_open2(codec_ctx, codec, NULL);
//申请一个帧结构体
AVFrame *pFrame = av_frame_alloc();
int frameFinished;
while (av_read_frame(ifmt_ctx, &pkg) >= 0) {
if (pkg.stream_index != video_stream_index) {
continue;
}
ret = avcodec_decode_video2(codec_ctx, pFrame, &frameFinished, &pkg);
if (!frameFinished)
continue;
}
jni创建 Bitmap
jobject createBitmap(JNIEnv *env,
int width, int height) {
jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls,
"createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jstring configName = env->NewStringUTF("ARGB_8888");
jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass,
"valueOf",
"(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass,
valueOfBitmapConfigFunction,
configName);
jobject newBitmap = env->CallStaticObjectMethod(bitmapCls,
createBitmapFunction,
width, height,
bitmapConfig);
return newBitmap;
}
使用 LibYUV 转换 RGB 格式
先使用#include <android/bitmap.h>头文件中 AndroidBitmap_lockPixels() 的函数绑定像素指针的地址,使用 libyuv 中的 I420ToABGR() 函数将 yuv420p 转换为 argb,记得最后使用 AndroidBitmap_unlockPixels() 函数回收Bitmap。
jobject bmp;
bmp = createBitmap(env, codec_ctx->width, codec_ctx->height);
void *addr_pixels;
ret = AndroidBitmap_lockPixels(env, bmp, &addr_pixels);
//yuv420p to argb
int linesize = pFrame->width * 4;
libyuv::I420ToABGR(pFrame->data[0], pFrame->linesize[0], // Y
pFrame->data[1], pFrame->linesize[1], // U
pFrame->data[2], pFrame->linesize[2], // V
(uint8_t *) addr_pixels, linesize, // RGBA
pFrame->width, pFrame->height);
AndroidBitmap_unlockPixels(env, bmp);
释放资源
``
av_packet_unref(&pkg);
av_free(pFrame);
avcodec_close(codec_ctx);
avformat_close_input(&ifmt_ctx);
env->ReleaseStringUTFChars(path, _path); `