http://www.linuxfromscratch.org/blfs/view/svn/multimedia/transcode.html
将transcode中的avilib文件复制到项目中。
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
在avilib创建CMakeLists.txt
add_library( # Sets the name of the library.
avi-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
avilib.c
platform_posix.c)
提取共用代码抽象成一个类
public abstract class AbstractPlayerActivity extends Activity {
public static final String EXTRA_FILE_NAME = "cn.study.aviplayer.EXTRA_FILE_NAME";
protected long avi = 0;
@Override
protected void onStart() {
super.onStart();
try {
avi = open(getFileName());
} catch (IOException e) {
new AlertDialog.Builder(this)
.setTitle("错误提示")
.setMessage(e.getMessage())
.show();
}
}
@Override
protected void onStop() {
super.onStop();
if (0 != avi) {
close(avi);
avi = 0;
}
}
protected String getFileName() {
return getIntent().getExtras().getString(EXTRA_FILE_NAME);
}
/**
* 打开指定的avi文件
*
* @return
*/
protected native static long open(String fileName) throws IOException;
/**
* 获取视频宽度
*
* @param avi
* @return
*/
protected native static int getWidth(long avi);
/**
* 获取视频高度
*
* @param avi
* @return
*/
protected native static int getHeight(long avi);
/**
* 获取帧速
*
* @param avi
* @return
*/
protected native static double getFrameRate(long avi);
/**
* 基于给定的文件描述符关闭指定的avi文件
*
* @param avi
*/
protected native static void close(long avi);
}
使用javah来生成.h文件。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class cn_study_aviplayer_AbstractPlayerActivity */
#ifndef _Included_cn_study_aviplayer_AbstractPlayerActivity
#define _Included_cn_study_aviplayer_AbstractPlayerActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: cn_study_aviplayer_AbstractPlayerActivity
* Method: open
* Signature: (Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL Java_cn_study_aviplayer_AbstractPlayerActivity_open
(JNIEnv *, jclass, jstring);
/*
* Class: cn_study_aviplayer_AbstractPlayerActivity
* Method: getWidth
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_cn_study_aviplayer_AbstractPlayerActivity_getWidth
(JNIEnv *, jclass, jlong);
/*
* Class: cn_study_aviplayer_AbstractPlayerActivity
* Method: getHeight
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_cn_study_aviplayer_AbstractPlayerActivity_getHeight
(JNIEnv *, jclass, jlong);
/*
* Class: cn_study_aviplayer_AbstractPlayerActivity
* Method: getFrameRate
* Signature: (J)D
*/
JNIEXPORT jdouble JNICALL Java_cn_study_aviplayer_AbstractPlayerActivity_getFrameRate
(JNIEnv *, jclass, jlong);
/*
* Class: cn_study_aviplayer_AbstractPlayerActivity
* Method: close
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_cn_study_aviplayer_AbstractPlayerActivity_close
(JNIEnv *, jclass, jlong);
#ifdef __cplusplus
}
#endif
#endif
在native-lib.cpp中导入avilib.h需要。
extern "C" {
#include "avilib/avilib.h"
}
native-lib.cpp
#include
#include
#include "cn_study_aviplayer_AbstractPlayerActivity.h"
#include "Common.h"
#include
extern "C" {
#include "avilib/avilib.h"
}
extern "C" JNIEXPORT jstring JNICALL
Java_cn_study_aviplayer_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jlong JNICALL
Java_cn_study_aviplayer_AbstractPlayerActivity_open(JNIEnv *env, jclass clazz, jstring file_name) {
avi_t *avi = 0;
//获取文件名字并转换成字符串变量
const char *cFileName = env->GetStringUTFChars(file_name, 0);
if (0 == cFileName) {
goto exit;
}
avi = AVI_open_input_file(cFileName, 1);
env->ReleaseStringUTFChars(file_name, cFileName);
if (0 == avi) {
ThrowException(env, "java/io/IOException", AVI_strerror());
}
exit:
return (jlong) avi;
}
extern "C"
JNIEXPORT jint JNICALL
Java_cn_study_aviplayer_AbstractPlayerActivity_getWidth(JNIEnv *env, jclass clazz, jlong avi) {
return AVI_video_width((avi_t *) avi);
}
extern "C"
JNIEXPORT jint JNICALL
Java_cn_study_aviplayer_AbstractPlayerActivity_getHeight(JNIEnv *env, jclass clazz, jlong avi) {
return AVI_video_height((avi_t *) avi);
}
extern "C"
JNIEXPORT jdouble JNICALL
Java_cn_study_aviplayer_AbstractPlayerActivity_getFrameRate(JNIEnv *env, jclass clazz, jlong avi) {
return AVI_frame_rate((avi_t *) avi);
}
extern "C"
JNIEXPORT void JNICALL
Java_cn_study_aviplayer_AbstractPlayerActivity_close(JNIEnv *env, jclass clazz, jlong avi) {
AVI_close((avi_t *) avi);
}
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp
Common.cpp
cn_study_aviplayer_BitmapPlayerActivity.cpp)
#添加子目录
add_subdirectory(avilib)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
find_library( # Sets the name of the path variable.
jnigraphics-lib
jnigraphics)
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${
jnigraphics-lib}#开启jnigraphics
${
log-lib}
avi-lib)
检索bitmap对象信息,如大小、像素格式。成功返回0,失败返回一个负数。
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,AndroidBitmapInfo* info);
访问原生像素缓存,锁定像素缓存以确保像素的内存不会移动。
成功返回0,失败返回负数。
int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);
释放原生像素缓存
成功返回0,失败返回负数
int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
public class BitmapPlayerActivity extends AbstractPlayerActivity {
private final AtomicBoolean isPlaying = new AtomicBoolean();
private SurfaceHolder surfaceHolder;
private SurfaceView surfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bitmap_player);
surfaceView = findViewById(R.id.surface_view);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(shCallback);
}
private final SurfaceHolder.Callback shCallback = new SurfaceHolder.Callback2() {
@Override
public void surfaceRedrawNeeded(SurfaceHolder holder) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isPlaying.set(true);
new Thread(renderer).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isPlaying.set(false);
}
};
private final Runnable renderer = new Runnable() {
@Override
public void run() {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
int width = getWidth(avi);
int height = getHeight(avi);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
float sx = 2.0f;
float sy = 2.0f;
Matrix matrix = new Matrix();
matrix.postScale(sx, sy);
int vWidth = (int) (width * sx);
int vHeight = (int) (height * sy);
int x = (1080 - vWidth) / 2;
int y = (1920 - vHeight) / 2;
matrix.postTranslate(x, y);
//延迟
long frameDelay = (long) (1000 / getFrameRate(avi));
while (isPlaying.get()) {
render(avi, bitmap);
Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bitmap, matrix, null);
surfaceHolder.unlockCanvasAndPost(canvas);
//等待下一针
try {
Thread.sleep(frameDelay);
} catch (InterruptedException e) {
break;
}
}
}
};
/**
* 从avi文件描述符输出到指定bitmap来渲染帧
*
* @param avi
* @param bitmap
* @return
*/
private native static boolean render(long avi, Bitmap bitmap);
}
cn_study_aviplayer_BitmapPlayerActivity.cpp
#include "Common.h"
#include "cn_study_aviplayer_BitmapPlayerActivity.h"
extern "C" {
#include "avilib/avilib.h"
}
#include
extern "C"
JNIEXPORT jboolean JNICALL
Java_cn_study_aviplayer_BitmapPlayerActivity_render(JNIEnv *env, jclass clazz, jlong avi,
jobject bitmap) {
jboolean isFrameRead = JNI_FALSE;
char *frameBuffer = 0;
long frameSize = 0;
int keyFrame = 0;
if (0 > AndroidBitmap_lockPixels(env, bitmap, (void **) &frameBuffer)) {
ThrowException(env, "java/io/IOException", "不能够锁定像素");
goto exit;
}
//将avi帧byte读取到bitmap中
frameSize = AVI_read_frame((avi_t *) avi, frameBuffer, &keyFrame);
//解锁
if (0 > AndroidBitmap_unlockPixels(env, bitmap)) {
ThrowException(env, "java/io/IOException", "不能够解锁像素");
goto exit;
}
//是否读取成功
if (0 < frameSize) {
isFrameRead = JNI_TRUE;
}
exit:
return isFrameRead;
}
想使用这个avi播放器,视频文件格式必须是avi格式,希望视频负载是通过RGB565颜色空间的未压缩原始帧来提供的。
将视频放到指定目录,运行效果如下:
详细代码和视频文件
https://gitee.com/xd_box/AviPlayer
file>Setting>Tools>External Tools 点击+号。
program:$JDKPath$\bin\javah.exe
arguments:-classpath . -jni -d $ModuleFileDir$\src\main\cpp $FileClass$
working directory:$ModuleFileDir$\src\main\java