目前正在开发一个基于Android的移动编辑项目,项目中需要实现对视频叠加字幕的功能,参考了一些文章,暂定采用OpenCV库,并通过NDK方式来实现。
我的操作系统为Windows7 x64,C++开发环境使用的是VC2005。
搭建NDK+OpenCV开发环境,首先需要从官网下载相关库。我下载的NDK版本是android-ndk-r9-windows-x86,OpenCV版本是OpenCV-2.4.9。
NDK的配置:
1. 解压NDK包到某个目录下;
2. 在Android项目的根目录下新建名为jni的VC工程,并添加ndk_demo.cpp文件。注意,有些教程说NDK不支持C++,实际是可以支持的,只要在cpp文件中使用extern "C"关键字,要求编译器将这里面的代码按C语言的方式编译即可;
3. 在ndk_demo.cpp中首先声明jni接口,例如
//ndk includes
#include <jni.h>
// jni interface declare
extern "C"
{
jstring Java_com_liuy_ndkdemo_MainActivity_ndkTest(JNIEnv* env, jobject thiz, jstring value);
};
然后在extern "C"后面完成函数实现。在函数实现中,就可以调用我们自己定义的C++类了。
注意函数的命名规则:Java_【调用NDK的类所在的包】_【调用NDK的类名】_【NDK方法名】。env和thiz这两个参数是默认的,如果要增加参数,写在它俩的后面即可,如那个jstring value;
此外,对于C++,调用的函数和C是不同的。很多的教程只是写了对C的调用,对于C++,照猫画虎会编译报错的。例如,将入参数的jstring转换为const char*,
在.c文件中,要写成:env->GetStringUTFChars(env, value,0);
而在.cpp文件中,则应该是:env->GetStringUTFChars(value,0);
4. 编写Android.mk和Application.mk文件。由于OpenCV库需要修改Android.mk文件里面的内容,因此这部分放到后面说;
5. 在jni目录下新建一个名为build.bat的文件,编辑其中的内容:
e: 【Android项目所在的盘】
cd e:\Projects\Android\NDKDemo\jni 【项目中jni工程的路径】
e:\Projects\Android\android-ndk-r9-windows-x86\ndk-build.cmd 【NDK编译器路径】
我们可以通过执行这个bat文件,来编译so库。在VisualStudio中,可以将“生成事件”—“预生成事件”设置为这个build.bat,然后按F7就可以编译了。注意,VC自己的编译器也会试图编译这些文件,可能会报错,不予理会即可。
编译完成后,会在Android项目的libs\armeabi-v7a目录下生成.so动态链接库文件,并自动生成一个obj文件夹,如下图;
至此,NDK环境就设置完成了。
下面是OpenCV的配置:
1. 解压OpenCV包;
2. 在jni目录下编写Android.mk和Application.mk文件,
#Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OpenCV_INSTALL_MODULES := on
OPENCV_CAMERA_MODULES := off
OPENCV_LIB_TYPE := STATIC
include ../../OpenCV-2.4.9/jni/OpenCV.mk
LOCAL_MODULE := ndk_demo 【生成的文件名为:lib+这个名字+.so】
LOCAL_SRC_FILES := ndk_demo.cpp 【要编译的文件,如果有多个不要漏了】
LOCAL_LDLIBS += -lm –llog
include $(BUILD_SHARED_LIBRARY)
红字部分是和OpenCV相关的。要特别注意OpenCV库和工程目录的位置关系,否则会导致找不到OpenCV.mk文件,无法编译通过。
#Application.mk
APP_ABI:= armeabi-v7a
APP_STL:= gnustl_static
APP_CPPFLAGS:= -frtti -fexceptions
APP_PLATFORM:= android-9
至此,全部搭建工作就完成了。
可以试着通过OpenCV往图片上加一行文字,来验证下:
//opencv includes
#include <highgui.h>
void Java_com_liuy_ndkdemo_MainActivity_savePngByOpenCV(JNIEnv* env, jobject thiz, jstring filePath)
{
const char* cFilePath = env->GetStringUTFChars(filePath,0);
IplImage* pImg= cvLoadImage(cFilePath);
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_COMPLEX, 1, 1, 1, 2, 8);
cvPutText(pImg,"This word is added by NDK !", cvPoint(100, 100), &font,CV_RGB(255,0,0));
cvSaveImage("result.png",pImg);
delete cDstFilePath;
cDstFilePath = NULL;
env->ReleaseStringUTFChars(filePath, cFilePath);
}
效果如下:
对于视频处理方面,本想用VideoCapture或cvGetCaptureProperty来根据文件路径直接读取视频数据,但调用它们总是读取失败,具体原因还不得而知。还需要深究下。