You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需FFmpeg和OpenCV,NDK是否有可提取视频帧的原生API?

NDK原生视频帧提取及色彩调整实现方案

API支持说明

NDK原生提供了无第三方依赖的视频处理能力,你的需求完全可以实现:

  • 帧提取可使用两类原生API:
    • AMediaMetadataRetriever:API 24及以上支持,对应Java层MediaMetadataRetriever的原生实现,适合按时间戳取指定帧、简单逐帧提取场景
    • AMediaExtractor + AMediaCodec组合:API 21及以上支持,通过硬解码实现全量逐帧提取,性能更高、可控性更强
  • 色彩调整可直接操作提取到的帧内存数据完成,无需额外依赖库

编译配置

需要在CMakeLists.txt中链接NDK原生媒体库:

find_library(media-lib mediandk)
find_library(bitmap-lib jnigraphics)
target_link_libraries(
        你的原生模块名
        ${media-lib}
        ${bitmap-lib}
)

代码示例

1. 按时间戳提取视频帧

#include <jni.h>
#include <media/NdkMediaMetadataRetriever.h>
#include <android/bitmap.h>
#include <algorithm>

extern "C" JNIEXPORT jobject JNICALL
Java_com_your_package_VideoUtils_getFrameAtTime(JNIEnv *env, jobject thiz, jstring video_path, jlong time_us) {
    const char *path = env->GetStringUTFChars(video_path, nullptr);
    AMediaMetadataRetriever *retriever = AMediaMetadataRetriever_create();
    
    // 设置数据源
    media_status_t status = AMediaMetadataRetriever_setDataSource(retriever, path, nullptr);
    if (status != AMEDIA_OK) {
        AMediaMetadataRetriever_delete(retriever);
        env->ReleaseStringUTFChars(video_path, path);
        return nullptr;
    }

    AImage *image = nullptr;
    // 取帧模式:1=获取最近的帧(含非关键帧),0=最近关键帧,2=下一关键帧
    status = AMediaMetadataRetriever_getFrameAtTime(retriever, time_us, 1, &image);
    if (status != AMEDIA_OK || !image) {
        AMediaMetadataRetriever_delete(retriever);
        env->ReleaseStringUTFChars(video_path, path);
        return nullptr;
    }

    // 读取图像参数
    int32_t width, height;
    AImage_getWidth(image, &width);
    AImage_getHeight(image, &height);

    // 创建RGBA格式Bitmap
    AndroidBitmapInfo bitmap_info{};
    bitmap_info.width = width;
    bitmap_info.height = height;
    bitmap_info.stride = width * 4;
    bitmap_info.format = ANDROID_BITMAP_FORMAT_RGBA_8888;
    jobject bitmap = nullptr;
    if (AndroidBitmap_createBitmap(env, &bitmap_info, nullptr, &bitmap) != ANDROID_BITMAP_RESULT_SUCCESS) {
        AImage_delete(image);
        AMediaMetadataRetriever_delete(retriever);
        env->ReleaseStringUTFChars(video_path, path);
        return nullptr;
    }

    // 拷贝帧数据到Bitmap
    void *bitmap_buf = nullptr;
    AndroidBitmap_lockPixels(env, bitmap, &bitmap_buf);
    uint8_t *image_buf = nullptr;
    int32_t row_stride = 0;
    AImage_lockImageBuffer(image, &image_buf, &row_stride);
    memcpy(bitmap_buf, image_buf, width * height * 4);
    AImage_unlockImageBuffer(image);
    AndroidBitmap_unlockPixels(env, bitmap);

    // 释放资源
    AImage_delete(image);
    AMediaMetadataRetriever_delete(retriever);
    env->ReleaseStringUTFChars(video_path, path);
    return bitmap;
}

2. 帧色彩调整示例

以下为亮度调整的示例代码,可直接扩展实现对比度、饱和度、色调等调整逻辑:

extern "C" JNIEXPORT void JNICALL
Java_com_your_package_VideoUtils_adjustFrameBrightness(JNIEnv *env, jobject thiz, jobject bitmap, jint brightness) {
    AndroidBitmapInfo info{};
    if (AndroidBitmap_getInfo(env, bitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS || 
        info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
        return;
    }

    void *pixels = nullptr;
    AndroidBitmap_lockPixels(env, bitmap, &pixels);
    auto *buf = static_cast<uint8_t*>(pixels);
    int pixel_count = info.width * info.height;

    for (int i = 0; i < pixel_count; ++i) {
        int r = buf[i * 4];
        int g = buf[i * 4 + 1];
        int b = buf[i * 4 + 2];
        // 限制数值范围在0-255之间
        r = std::clamp(r + brightness, 0, 255);
        g = std::clamp(g + brightness, 0, 255);
        b = std::clamp(b + brightness, 0, 255);
        buf[i * 4] = r;
        buf[i * 4 + 1] = g;
        buf[i * 4 + 2] = b;
    }

    AndroidBitmap_unlockPixels(env, bitmap);
}

额外说明

如果需要全量逐帧提取整个视频的所有帧,建议使用AMediaExtractor解封装+AMediaCodec硬解码的方案,性能远高于循环调用AMediaMetadataRetriever取帧,同样无第三方依赖。

内容的提问来源于stack exchange,提问作者Justin Wood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 19:39:02