NDK学习笔记(十二) 原生图形api,使用AVILib创建一个AVI视频播放器

1.练练手,使用AVILib创建一个AVI视频播放器

(1)首先下载transcode,并配置avilib。

http://www.linuxfromscratch.org/blfs/view/svn/multimedia/transcode.html

将transcode中的avilib文件复制到项目中。

在这里插入图片描述
编辑platform.h文件

#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)

(2)创建AbstractPlayerActivity并实现native方法

提取共用代码抽象成一个类

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 <jni.h>
/* 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 <jni.h>
#include <string>

#include "cn_study_aviplayer_AbstractPlayerActivity.h"
#include "Common.h"
#include <android/bitmap.h>

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);
}

(3)配置native-lib,开启jnigraphics,连接avi-lib

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)

(4)jni graphics api

检索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);

(5)使用bitmap渲染来更新avi player

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 <android/bitmap.h>

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

2.android studio 配置javah

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

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值