Android Codec2 Filter 算法模块开发指南
目录
概述
【个人见解】Codec2-Pipeline 和 GSteamer、SPF(QCOM ADSP框架)、各类AI推理网络(onnx qnn snpe ncnn mnn 等)pipeline 有异曲同工之处
本文档详细介绍如何基于 Android Codec2 Filter 框架开发自定义算法模块,包括算法封装、Plugin 实现以及 JNI或、JAVA侧 调用的完整流程。
核心概念
- Codec2 Component: Android 多媒体框架的核心组件
- Filter Plugin: 可插拔的滤镜插件机制
- C2Component: Codec2 组件接口
- FilterWrapper: 组件包装器,管理 filter 生命周期
架构设计
整体架构图
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Application │ │ MediaCodec │ │ Display │
│ │◄───│ │◄───│ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ FilterWrapper │
│ │
└──────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Decoder │ │ Filter 1 │ │ Filter 2 │
│ │ │ │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
数据流向
Input Buffer → Decoder → Filter Chain → Output Buffer
│ │
▼ ▼
解码处理 算法处理(如:降噪、增强、转换)
算法模块封装流程
步骤 1: 定义算法接口
首先定义算法处理的核心接口:
cpp
// AlgorithmInterface.h
#pragma once
#include <memory>
#include <vector>
#include <cstdint>
namespace myalgorithm {
// 算法配置参数
struct AlgorithmConfig {
int width;
int height;
int format; // 像素格式
float strength; // 处理强度
bool enable_gpu; // 是否使用 GPU 加速
};
// 算法处理结果
enum AlgorithmResult {
SUCCESS = 0,
ERROR_INVALID_INPUT = -1,
ERROR_PROCESSING = -2,
ERROR_UNSUPPORTED_FORMAT = -3,
};
// 算法处理接口
class IAlgorithmProcessor {
public:
virtual ~IAlgorithmProcessor() = default;
// 初始化算法
virtual AlgorithmResult init(const AlgorithmConfig& config) = 0;
// 处理单帧
virtual AlgorithmResult process(
const uint8_t* input_data,
uint8_t* output_data,
int stride) = 0;
// 释放资源
virtual void release() = 0;
// 获取处理延迟
virtual int getLatency() const = 0;
};
// 工厂函数
std::unique_ptr<IAlgorithmProcessor> createProcessor();
} // namespace myalgorithm
步骤 2: 实现核心算法
实现具体的算法处理逻辑:
cpp
// MyAlgorithmProcessor.cpp
#include "AlgorithmInterface.h"
#include <android-base/logging.h>
#include <chrono>
#ifdef USE_GPU
#include <GLES3/gl3.h>
#include <EGL/egl.h>
#endif
namespace myalgorithm {
class MyAlgorithmProcessor : public IAlgorithmProcessor {
public:
MyAlgorithmProcessor() = default;
~MyAlgorithmProcessor() override { release(); }
AlgorithmResult init(const AlgorithmConfig& config) override {
if (config.width <= 0 || config.height <= 0) {
return ERROR_INVALID_INPUT;
}
config_ = config;
// 初始化 CPU 处理
if (!initCPU()) {
return ERROR_PROCESSING;
}
// 如果启用 GPU,初始化 GPU 处理
if (config.enable_gpu) {
if (!initGPU()) {
LOG(WARNING) << "GPU initialization failed, falling back to CPU";
config_.enable_gpu = false;
}
}
initialized_ = true;
return SUCCESS;
}
AlgorithmResult process(
const uint8_t* input_data,
uint8_t* output_data,
int stride) override {
if (!initialized_ || !input_data || !output_data) {
return ERROR_INVALID_INPUT;
}
auto start = std::chrono::high_resolution_clock::now();
AlgorithmResult result;
if (config_.enable_gpu) {
result = processGPU(input_data, output_data, stride);
} else {
result = processCPU(input_data, output_data, stride);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
last_process_time_ = duration.count();
return result;
}
void release() override {
if (initialized_) {
releaseCPU();
if (config_.enable_gpu) {
releaseGPU();
}
initialized_ = false;
}
}
int getLatency() const override {
return last_process_time_;
}
private:
bool initCPU() {
// CPU 处理初始化
// 例如:分配临时缓冲区、初始化算法参数等
temp_buffer_.resize(config_.width * config_.height * 4);
return true;
}
bool initGPU() {
#ifdef USE_GPU
// GPU 初始化逻辑
// 创建 EGL 上下文、编译着色器等
return initEGL() && initShaders();
#else
return false;
#endif
}
AlgorithmResult processCPU(const uint8_t* input, uint8_t* output, int stride) {
// CPU 算法处理实现
// 示例:简单的图像增强算法
for (int y = 0; y < config_.height; ++y) {
for (int x = 0; x < config_.width; ++x) {
int idx = y * stride + x;
// 简单的亮度增强
int pixel = input[idx];
pixel = static_cast<int>(pixel * config_.strength);
pixel = std::min(255, std::max(0, pixel));
output[idx] = static_cast<uint8_t>(pixel);
}
}
return SUCCESS;
}
AlgorithmResult processGPU(const uint8_t* input, uint8_t* output, int stride) {
#ifdef USE_GPU
// GPU 处理实现
// 使用 OpenGL ES 进行并行处理
return processWithOpenGL(input, output, stride);
#else
return ERROR_UNSUPPORTED_FORMAT;
#endif
}
void releaseCPU() {
temp_buffer_.clear();
}
void releaseGPU() {
#ifdef USE_GPU
// 释放 GPU 资源
if (egl_context_ != EGL_NO_CONTEXT) {
eglDestroyContext(egl_display_, egl_context_);
}
#endif
}
#ifdef USE_GPU
bool initEGL() {
// EGL 初始化代码
egl_display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (egl_display_ == EGL_NO_DISPLAY) {
return false;
}
EGLint major, minor;
if (!eglInitialize(egl_display_, &major, &minor)) {
return false;
}
// 配置 EGL
EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_NONE
};
EGLint num_configs;
EGLConfig config;
if (!eglChooseConfig(egl_display_, config_attribs, &config, 1, &num_configs)) {
return false;
}
EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
egl_context_ = eglCreateContext(egl_display_, config, EGL_NO_CONTEXT, context_attribs);
return egl_context_ != EGL_NO_CONTEXT;
}
bool initShaders() {
// 着色器编译和链接
const char* vertex_shader_source =
"attribute vec4 position;\n"
"attribute vec2 texcoord;\n"
"varying vec2 v_texcoord;\n"
"void main() {\n"
" gl_Position = position;\n"
" v_texcoord = texcoord;\n"
"}\n";
const char* fragment_shader_source =
"precision mediump float;\n"
"varying vec2 v_texcoord;\n"
"uniform sampler2D texture;\n"
"uniform float strength;\n"
"void main() {\n"
" vec4 color = texture2D(texture, v_texcoord);\n"
" color.rgb *= strength;\n"
" gl_FragColor = color;\n"
"}\n";
// 编译着色器代码...
return compileShaders(vertex_shader_source, fragment_shader_source);
}
AlgorithmResult processWithOpenGL(const uint8_t* input, uint8_t* output, int stride) {
// OpenGL 处理实现
// 1. 创建纹理
// 2. 上传数据
// 3. 执行处理
// 4. 读取结果
return SUCCESS;
}
#endif
private:
AlgorithmConfig config_;
bool initialized_ = false;
std::vector<uint8_t> temp_buffer_;
int last_process_time_ = 0;
#ifdef USE_GPU
EGLDisplay egl_display_ = EGL_NO_DISPLAY;
EGLContext egl_context_ = EGL_NO_CONTEXT;
GLuint program_ = 0;
GLuint input_texture_ = 0;
GLuint output_texture_ = 0;
#endif
};
std::unique_ptr<IAlgorithmProcessor> createProcessor() {
return std::make_unique<MyAlgorithmProcessor>();
}
} // namespace myalgorithm
步骤 3: 封装为 Codec2 Component
将算法封装为 Codec2 组件:
cpp
// MyAlgorithmC2Component.h
#pragma once
#include <C2Component.h>
#include <C2Config.h>
#include "AlgorithmInterface.h"
#include <memory>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <thread>
class MyAlgorithmC2Component : public C2Component,
public std::enable_shared_from_this<MyAlgorithmC2Component> {
public:
MyAlgorithmC2Component(c2_node_id_t id,
const std::shared_ptr<C2ReflectorHelper> &reflector);
~MyAlgorithmC2Component() override;
// C2Component 接口
c2_status_t setListener_vb(const std::shared_ptr<Listener> &listener,
c2_blocking_t mayBlock) override;
c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
c2_status_t announce_nb(const std::vector<C2WorkOutline> &) override;
c2_status_t flush_sm(flush_mode_t mode,
std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
c2_status_t drain_nb(drain_mode_t mode) override;
c2_status_t start() override;
c2_status_t stop() override;
c2_status_t reset() override;
c2_status_t release() override;
std::shared_ptr<C2ComponentInterface> intf() override;
private:
// 内部状态
enum State {
STOPPED,
RUNNING,
RELEASED,
STARTING,
STOPPING,
RESETTING,
RELEASING,
};
// 处理线程函数
void processLoop();
c2_status_t processWork(std::unique_ptr<C2Work> &work);
// Buffer 管理
c2_status_t allocateOutputBuffer(std::shared_ptr<C2GraphicBlock> *block);
c2_status_t processGraphicBuffer(const C2GraphicView &srcView,
C2GraphicView &dstView);
// 状态管理
State mState = STOPPED;
std::mutex mStateMutex;
// 工作队列
std::mutex mQueueMutex;
std::condition_variable mQueueCondition;
std::list<std::unique_ptr<C2Work>> mQueue;
// 处理线程
std::thread mProcessThread;
bool mThreadRunning = false;
// 监听器
std::shared_ptr<Listener> mListener;
// 组件接口
std::shared_ptr<C2ComponentInterface> mInterface;
// 算法处理器
std::unique_ptr<myalgorithm::IAlgorithmProcessor> mProcessor;
// 配置参数
int mWidth = 0;
int mHeight = 0;
int mStride = 0;
C2PixelFormat mFormat = C2PixelFormat::YUV420;
// Buffer 池
std::shared_ptr<C2BlockPool> mBlockPool;
};
Plugin 实现详解
Plugin 架构
┌─────────────────────────────────────────┐
│ FilterPlugin_V1 │
│ ┌─────────────────────────────────────┐│
│ │ ComponentStore ││
│ │ ┌─────────────────────────────┐ ││
│ │ │ MyAlgorithmC2Component │ ││
│ │ │ │ ││
│ │ └─────────────────────────────┘ ││
│ └─────────────────────────────────────┘│
└─────────────────────────────────────────┘
完整 Plugin 实现
cpp
// MyAlgorithmFilterPlugin.cpp
#include <C2Component.h>
#include <C2Config.h>
#include <FilterWrapper.h>
#include "MyAlgorithmC2Component.h"
#include <android-base/logging.h>
#include <cutils/properties.h>
// 组件 Store 实现
class MyAlgorithmComponentStore : public C2ComponentStore {
public:
MyAlgorithmComponentStore() {
// 注册组件
registerComponent("my.algorithm.filter");
}
std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override {
std::vector<std::shared_ptr<const C2Component::Traits>> components;
auto traits = std::make_shared<C2Component::Traits>();
traits->name = "my.algorithm.filter";
traits->domain = C2Component::DOMAIN_VIDEO;
traits->kind = C2Component::KIND_OTHER;
traits->rank = 0x100; // 设置优先级
components.push_back(traits);
return components;
}
std::shared_ptr<C2ComponentInterface> createComponentInterface(
C2String name, c2_node_id_t id) override {
if (name == "my.algorithm.filter") {
auto reflector = std::make_shared<C2ReflectorHelper>();
auto component = std::make_shared<MyAlgorithmC2Component>(id, reflector);
return component->intf();
}
return nullptr;
}
c2_status_t createComponent(
C2String name,
std::shared_ptr<C2Component> *const component) override {
if (name == "my.algorithm.filter") {
auto reflector = std::make_shared<C2ReflectorHelper>();
*component = std::make_shared<MyAlgorithmC2Component>(
0, reflector); // 使用默认 node_id
return C2_OK;
}
return C2_NOT_FOUND;
}
c2_status_t query_sm(
const std::vector<C2Param*> &stackParams,
const std::vector<C2Param::Index> &heapParamIndices,
std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
// 查询 store 参数
return C2_OK;
}
c2_status_t config_sm(
const std::vector<C2Param*> ¶ms,
std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
// 配置 store 参数
return C2_OK;
}
std::vector<std::shared_ptr<const C2ParamDescriptor>>
querySupportedParams_nb() const override {
// 返回支持的参数描述符
return {};
}
c2_status_t querySupportedValues_sm(
std::vector<C2FieldSupportedValuesQuery> &fields) const override {
// 查询支持的字段值
return C2_OK;
}
private:
void registerComponent(const std::string &name) {
// 组件注册逻辑
}
};
// Plugin 实现
class MyAlgorithmFilterPlugin : public FilterPlugin_V1 {
public:
MyAlgorithmFilterPlugin() {
mStore = std::make_shared<MyAlgorithmComponentStore>();
LOG(INFO) << "MyAlgorithmFilterPlugin created";
}
~MyAlgorithmFilterPlugin() override {
LOG(INFO) << "MyAlgorithmFilterPlugin destroyed";
}
std::shared_ptr<C2ComponentStore> getComponentStore() override {
return mStore;
}
bool describe(C2String name, Descriptor *desc) override {
if (name == "my.algorithm.filter") {
desc->inputFormat = { C2FormatYUV420 };
desc->outputFormat = { C2FormatYUV420 };
return true;
}
return false;
}
bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf) override {
// 检查是否需要启用 filter
// 1. 检查系统属性
bool propEnabled = property_get_bool(
"vendor.my.algorithm.filter.enable", true);
if (!propEnabled) {
return false;
}
// 2. 检查编码格式
C2StreamMediaTypeSetting::input mediaType;
c2_status_t err = intf->query_vb({&mediaType}, {}, C2_DONT_BLOCK, nullptr);
if (err == C2_OK) {
// 支持的编码格式
if (mediaType.value == "video/avc" ||
mediaType.value == "video/hevc" ||
mediaType.value == "video/av01") {
return true;
}
}
// 3. 检查分辨率
C2StreamPictureSizeInfo::input pictureSize;
err = intf->query_vb({&pictureSize}, {}, C2_DONT_BLOCK, nullptr);
if (err == C2_OK) {
// 只处理特定分辨率范围的内容
if (pictureSize.width >= 1280 && pictureSize.height >= 720) {
return true;
}
}
return false;
}
c2_status_t queryParamsForPreviousComponent(
const std::shared_ptr<C2ComponentInterface> &intf,
std::vector<std::unique_ptr<C2Param>> *params) override {
// 如果需要向前一个组件查询参数,在这里实现
// 例如:查询解码器支持的输出格式
C2StreamPixelFormatInfo::output pixelFormat;
c2_status_t err = intf->query_vb({&pixelFormat}, {}, C2_DONT_BLOCK, nullptr);
if (err == C2_OK) {
// 可以根据解码器输出格式调整 filter 参数
}
return C2_OK;
}
private:
std::shared_ptr<C2ComponentStore> mStore;
};
// C 接口导出
extern "C" {
int32_t GetFilterPluginVersion() {
return 1;
}
void *CreateFilterPlugin() {
return new MyAlgorithmFilterPlugin();
}
void DestroyFilterPlugin(void *plugin) {
delete static_cast<MyAlgorithmFilterPlugin*>(plugin);
}
}
JNI 调用流程
JNI 架构
┌─────────────────┐
│ Java/Kotlin │
│ Application │
└─────────────────┘
│
▼
┌─────────────────┐
│ JNI Layer │
│ │
└─────────────────┘
│
▼
┌─────────────────┐
│ Native C++ │
│ Codec2 │
└─────────────────┘
│
▼
┌─────────────────┐
│ Filter Plugin │
│ │
└─────────────────┘
JNI 接口定义
cpp
// jni/MyAlgorithmFilter_jni.h
#pragma once
#include <jni.h>
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
// 初始化算法 filter
JNIEXPORT jboolean JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeInit(
JNIEnv *env,
jobject thiz,
jint width,
jint height,
jfloat strength);
// 处理 buffer
JNIEXPORT jboolean JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeProcess(
JNIEnv *env,
jobject thiz,
jobject inputBuffer,
jobject outputBuffer,
jint stride);
// 释放资源
JNIEXPORT void JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeRelease(
JNIEnv *env,
jobject thiz);
// 获取处理延迟
JNIEXPORT jint JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeGetLatency(
JNIEnv *env,
jobject thiz);
// 设置参数
JNIEXPORT jboolean JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeSetParameter(
JNIEnv *env,
jobject thiz,
jstring key,
jstring value);
#ifdef __cplusplus
}
#endif
JNI 实现(system uid签名)
cpp
// jni/MyAlgorithmFilter_jni.cpp
#include "MyAlgorithmFilter_jni.h"
#include "AlgorithmInterface.h"
#include <android-base/logging.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
// 全局算法处理器实例
static std::unique_ptr<myalgorithm::IAlgorithmProcessor> g_processor;
// Java 类名
static const char* const kClassPathName = "com/xiaomi/media/MyAlgorithmFilter";
// JNI 方法表
static const JNINativeMethod sMethods[] = {
{"nativeInit", "(IIF)Z", (void*)Java_com_xiaomi_media_MyAlgorithmFilter_nativeInit},
{"nativeProcess", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;I)Z",
(void*)Java_com_xiaomi_media_MyAlgorithmFilter_nativeProcess},
{"nativeRelease", "()V", (void*)Java_com_xiaomi_media_MyAlgorithmFilter_nativeRelease},
{"nativeGetLatency", "()I", (void*)Java_com_xiaomi_media_MyAlgorithmFilter_nativeGetLatency},
{"nativeSetParameter", "(Ljava/lang/String;Ljava/lang/String;)Z",
(void*)Java_com_xiaomi_media_MyAlgorithmFilter_nativeSetParameter},
};
// JNI 加载函数
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
// 注册 native 方法
jclass clazz = env->FindClass(kClassPathName);
if (clazz == nullptr) {
return JNI_ERR;
}
if (env->RegisterNatives(clazz, sMethods, sizeof(sMethods) / sizeof(sMethods[0])) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
// JNI 卸载函数
void JNI_OnUnload(JavaVM *vm, void *reserved) {
if (g_processor) {
g_processor->release();
g_processor.reset();
}
}
// 实现 JNI 方法
JNIEXPORT jboolean JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeInit(
JNIEnv *env,
jobject thiz,
jint width,
jint height,
jfloat strength) {
LOG(INFO) << "Initializing algorithm filter: "
<< width << "x" << height
<< ", strength: " << strength;
// 创建算法处理器
g_processor = myalgorithm::createProcessor();
if (!g_processor) {
LOG(ERROR) << "Failed to create algorithm processor";
return JNI_FALSE;
}
// 配置参数
myalgorithm::AlgorithmConfig config;
config.width = width;
config.height = height;
config.format = 0; // 默认格式
config.strength = strength;
config.enable_gpu = true; // 尝试使用 GPU
// 初始化处理器
myalgorithm::AlgorithmResult result = g_processor->init(config);
if (result != myalgorithm::SUCCESS) {
LOG(ERROR) << "Failed to initialize algorithm processor: " << result;
g_processor.reset();
return JNI_FALSE;
}
LOG(INFO) << "Algorithm filter initialized successfully";
return JNI_TRUE;
}
JNIEXPORT jboolean JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeProcess(
JNIEnv *env,
jobject thiz,
jobject inputBuffer,
jobject outputBuffer,
jint stride) {
if (!g_processor) {
LOG(ERROR) << "Algorithm processor not initialized";
return JNI_FALSE;
}
// 获取 direct buffer 地址
uint8_t* input_data = static_cast<uint8_t*>(
env->GetDirectBufferAddress(inputBuffer));
uint8_t* output_data = static_cast<uint8_t*>(
env->GetDirectBufferAddress(outputBuffer));
if (!input_data || !output_data) {
LOG(ERROR) << "Invalid buffer addresses";
return JNI_FALSE;
}
// 执行处理
myalgorithm::AlgorithmResult result = g_processor->process(
input_data, output_data, stride);
if (result != myalgorithm::SUCCESS) {
LOG(ERROR) << "Algorithm processing failed: " << result;
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT void JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeRelease(
JNIEnv *env,
jobject thiz) {
LOG(INFO) << "Releasing algorithm filter";
if (g_processor) {
g_processor->release();
g_processor.reset();
}
}
JNIEXPORT jint JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeGetLatency(
JNIEnv *env,
jobject thiz) {
if (!g_processor) {
return -1;
}
return g_processor->getLatency();
}
JNIEXPORT jboolean JNICALL
Java_com_xiaomi_media_MyAlgorithmFilter_nativeSetParameter(
JNIEnv *env,
jobject thiz,
jstring key,
jstring value) {
if (!g_processor) {
return JNI_FALSE;
}
// 转换 Java 字符串
const char* key_str = env->GetStringUTFChars(key, nullptr);
const char* value_str = env->GetStringUTFChars(value, nullptr);
if (!key_str || !value_str) {
if (key_str) env->ReleaseStringUTFChars(key, key_str);
if (value_str) env->ReleaseStringUTFChars(value, value_str);
return JNI_FALSE;
}
// 处理参数设置
bool success = true;
std::string key_cpp(key_str);
std::string value_cpp(value_str);
if (key_cpp == "strength") {
// 设置强度参数
try {
float strength = std::stof(value_cpp);
// 这里需要实现参数更新逻辑
LOG(INFO) << "Set strength to: " << strength;
} catch (...) {
success = false;
}
} else if (key_cpp == "enable_gpu") {
// 设置 GPU 启用状态
bool enable_gpu = (value_cpp == "true");
LOG(INFO) << "Set GPU enable: " << enable_gpu;
} else {
LOG(WARNING) << "Unknown parameter: " << key_cpp;
success = false;
}
env->ReleaseStringUTFChars(key, key_str);
env->ReleaseStringUTFChars(value, value_str);
return success ? JNI_TRUE : JNI_FALSE;
}
Java 层封装
java
// com/xiaomi/media/MyAlgorithmFilter.java
package com.xiaomi.media;
import java.nio.ByteBuffer;
public class MyAlgorithmFilter {
private static final String TAG = "MyAlgorithmFilter";
static {
System.loadLibrary("myalgorithmfilter_jni");
}
private boolean mInitialized = false;
private int mWidth;
private int mHeight;
/**
* 初始化算法 filter
* @param width 图像宽度
* @param height 图像高度
* @param strength 处理强度 (0.0 - 2.0)
* @return 是否初始化成功
*/
public boolean init(int width, int height, float strength) {
if (mInitialized) {
release();
}
mWidth = width;
mHeight = height;
mInitialized = nativeInit(width, height, strength);
return mInitialized;
}
/**
* 处理图像 buffer
* @param inputBuffer 输入 buffer (Direct ByteBuffer)
* @param outputBuffer 输出 buffer (Direct ByteBuffer)
* @param stride 图像步长
* @return 是否处理成功
*/
public boolean process(ByteBuffer inputBuffer, ByteBuffer outputBuffer, int stride) {
if (!mInitialized) {
throw new IllegalStateException("Filter not initialized");
}
if (!inputBuffer.isDirect() || !outputBuffer.isDirect()) {
throw new IllegalArgumentException("Buffers must be direct buffers");
}
return nativeProcess(inputBuffer, outputBuffer, stride);
}
/**
* 释放资源
*/
public void release() {
if (mInitialized) {
nativeRelease();
mInitialized = false;
}
}
/**
* 获取处理延迟 (微秒)
* @return 处理延迟
*/
public int getLatency() {
if (!mInitialized) {
return -1;
}
return nativeGetLatency();
}
/**
* 设置参数
* @param key 参数名
* @param value 参数值
* @return 是否设置成功
*/
public boolean setParameter(String key, String value) {
if (!mInitialized) {
return false;
}
return nativeSetParameter(key, value);
}
/**
* 设置处理强度
* @param strength 强度值 (0.0 - 2.0)
* @return 是否设置成功
*/
public boolean setStrength(float strength) {
return setParameter("strength", String.valueOf(strength));
}
/**
* 设置是否启用 GPU
* @param enable 是否启用
* @return 是否设置成功
*/
public boolean setGPUEnabled(boolean enable) {
return setParameter("enable_gpu", String.valueOf(enable));
}
@Override
protected void finalize() throws Throwable {
try {
release();
} finally {
super.finalize();
}
}
// Native 方法声明
private native boolean nativeInit(int width, int height, float strength);
private native boolean nativeProcess(ByteBuffer inputBuffer, ByteBuffer outputBuffer, int stride);
private native void nativeRelease();
private native int nativeGetLatency();
private native boolean nativeSetParameter(String key, String value);
}
使用示例
java
// 使用示例
public class MyActivity extends Activity {
private MyAlgorithmFilter mFilter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 初始化 filter
mFilter = new MyAlgorithmFilter();
boolean success = mFilter.init(1920, 1080, 1.5f);
if (success) {
// 设置参数
mFilter.setGPUEnabled(true);
// 创建 direct buffers
ByteBuffer inputBuffer = ByteBuffer.allocateDirect(1920 * 1080 * 3 / 2);
ByteBuffer outputBuffer = ByteBuffer.allocateDirect(1920 * 1080 * 3 / 2);
// 处理图像
boolean processed = mFilter.process(inputBuffer, outputBuffer, 1920);
if (processed) {
// 获取处理延迟
int latency = mFilter.getLatency();
Log.d(TAG, "Processing latency: " + latency + " microseconds");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mFilter != null) {
mFilter.release();
}
}
}
完整示例
编译配置
blueprint
// Android.bp
cc_library_shared {
name: "libmyalgorithmfilter",
vendor: true,
shared_libs: [
"libcodec2",
"libcodec2_components",
"liblog",
"libutils",
"libbase",
"libEGL",
"libGLESv2",
],
static_libs: [
"libarect",
],
srcs: [
"MyAlgorithmC2Component.cpp",
"MyAlgorithmFilterPlugin.cpp",
"MyAlgorithmProcessor.cpp",
],
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
"-DUSE_GPU=1",
],
include_dirs: [
"frameworks/av/media/codec2/components/include",
"frameworks/av/media/codec2/core/include",
"frameworks/av/media/codec2/vndk/include",
],
}
cc_library_shared {
name: "libmyalgorithmfilter_jni",
vendor: true,
shared_libs: [
"libmyalgorithmfilter",
"liblog",
"libutils",
"libnativehelper",
],
srcs: [
"jni/MyAlgorithmFilter_jni.cpp",
],
cflags: [
"-Wall",
"-Werror",
],
}
部署脚本
bash
#!/bin/bash
# deploy_filter.sh
# 编译
echo "Building algorithm filter..."
mmm vendor/xiaomi/proprietary/multimedia/algorithm-filter/
if [ $? -ne 0 ]; then
echo "Build failed!"
exit 1
fi
# 推送到设备
echo "Deploying to device..."
adb push out/target/product/$TARGET_PRODUCT/vendor/lib64/libmyalgorithmfilter.so /vendor/lib64/
adb push out/target/product/$TARGET_PRODUCT/vendor/lib64/libmyalgorithmfilter_jni.so /vendor/lib64/
# 创建符号链接
echo "Creating symbolic link..."
adb shell "ln -sf /vendor/lib64/libmyalgorithmfilter.so /vendor/lib64/libc2filterplugin.so"
# 设置权限
echo "Setting permissions..."
adb shell "chmod 644 /vendor/lib64/libmyalgorithmfilter.so"
adb shell "chmod 644 /vendor/lib64/libmyalgorithmfilter_jni.so"
# 启用 filter
echo "Enabling filter..."
adb shell "setprop vendor.my.algorithm.filter.enable true"
echo "Deployment completed!"
调试与优化
调试技巧
- 日志标签定义
cpp
#define LOG_TAG "MyAlgorithmFilter"
#define LOG_NDEBUG 0
#include <utils/Log.h>
- 性能监控
cpp
class PerformanceMonitor {
public:
void start() {
start_time_ = std::chrono::high_resolution_clock::now();
}
void end(const std::string& operation) {
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
end_time - start_time_);
ALOGD("%s took %lld microseconds", operation.c_str(), duration.count());
}
private:
std::chrono::high_resolution_clock::time_point start_time_;
};
- 内存监控
cpp
void logMemoryUsage() {
std::ifstream status("/proc/self/status");
std::string line;
while (std::getline(status, line)) {
if (line.find("VmSize") != std::string::npos ||
line.find("VmRSS") != std::string::npos) {
ALOGD("%s", line.c_str());
}
}
}
性能优化
-
GPU 优化
- 使用纹理缓存
- 批量处理多帧
- 优化着色器代码
-
CPU 优化
- 使用 SIMD 指令
- 多线程并行处理
- 内存对齐
-
内存优化
- 使用内存池
- 避免频繁分配释放
- 使用零拷贝技术
JAVA侧 CODEC 调用(user)
java
// Java 应用层代码
MediaCodec codec = null;
try {
// 关键点:直接使用你在 HAL 层注册的名字
codec = MediaCodec.createByCodecName("c2.myvendor.custom.avc.decoder");
// 后续 configure, start, queueInputBuffer, dequeueOutputBuffer 完全标准流程
// MediaFormat format;
// codec.configure(format, surface, null, 0);
// codec.start();
// ... 正常使用
} catch (IOException e) {
Log.e("MyCodec", "Failed to create custom codec", e);
}
常见问题
1. Filter 未生效
问题: Filter 编译部署后没有生效
解决方案:
bash
# 检查符号链接
adb shell "ls -la /vendor/lib64/libc2filterplugin.so"
# 检查系统属性
adb shell "getprop vendor.my.algorithm.filter.enable"
# 检查日志
adb logcat -s MyAlgorithmFilter:* Codec2-FilterWrapper:*
2. 内存泄漏
问题: 长时间运行后内存持续增长
解决方案:
- 使用智能指针管理资源
- 实现正确的析构函数
- 使用内存检测工具
3. 性能问题
问题: 处理延迟过高
解决方案:
- 启用 GPU 加速
- 优化算法复杂度
- 使用性能分析工具
4. 兼容性问题
问题: 在某些设备上无法运行
解决方案:
- 检查硬件支持
- 提供降级方案
- 测试不同平台
总结
通过本文档的详细介绍,你应该能够:
- 理解 Android Codec2 Filter 的架构和工作原理
- 封装自己的算法模块为 Codec2 组件
- 实现 Filter Plugin 并集成到系统中
- 通过 JNI 接口在 Java 层调用算法功能
- 进行调试和性能优化
关键要点:
- 正确实现 C2Component 接口
- 合理管理 buffer 和内存
- 处理好线程同步
- 提供完善的错误处理
- 进行充分的测试和优化
希望这份指南能帮助你成功开发自己的算法 filter 模块!