zoukankan      html  css  js  c++  java
  • [原]如何在Android用FFmpeg解码图像

    前一篇[原]如何用Android NDK编译FFmpeg 我们知道了如何使用NDK来编译Android平台下使用的FFmpeg动态库。这篇文章我们就可以使用Android下的JNI来调用FFMpeg进行解码了。

    一、编译出来可以使用的动态库,我们会看到如下输出则表示link完成了:

      

    CC    libavcodec/log2_tab.o
    CC    libavutil/log2_tab.o
    CC    libswresample/log2_tab.o
    AR    libavcodec/libavcodec.a
    LD    libavutil/libavutil.so.52
    AR    libavutil/libavutil.a
    AR    libswresample/libswresample.a
    LD    libavcodec/libavcodec.so.55
    LD    libswresample/libswresample.so.0
    LD    libswscale/libswscale.so.2
    LD    libavformat/libavformat.so.55
    INSTALL    libavformat/libavformat.a
    INSTALL    libavformat/libavformat.so
    STRIP    install-libavformat-shared
    INSTALL    libavcodec/libavcodec.a
    INSTALL    libavcodec/libavcodec.so
    STRIP    install-libavcodec-shared
    INSTALL    libswresample/libswresample.a
    INSTALL    libswresample/libswresample.so
    STRIP    install-libswresample-shared
    INSTALL    libswscale/libswscale.a
    INSTALL    libswscale/libswscale.so
    STRIP    install-libswscale-shared
    INSTALL    libavutil/libavutil.a
    INSTALL    libavutil/libavutil.so
    STRIP    install-libavutil-shared
    INSTALL    libavformat/avformat.h
    INSTALL    libavformat/avio.h
    INSTALL    libavformat/version.h
    INSTALL    libavformat/libavformat.pc
    INSTALL    libavcodec/avcodec.h
    INSTALL    libavcodec/avfft.h
    INSTALL    libavcodec/dxva2.h
    INSTALL    libavcodec/old_codec_ids.h
    INSTALL    libavcodec/vaapi.h
    INSTALL    libavcodec/vda.h
    INSTALL    libavcodec/vdpau.h
    INSTALL    libavcodec/version.h
    INSTALL    libavcodec/xvmc.h
    INSTALL    libavcodec/libavcodec.pc
    INSTALL    libswresample/swresample.h
    INSTALL    libswresample/version.h
    INSTALL    libswresample/libswresample.pc
    INSTALL    libswscale/swscale.h
    INSTALL    libswscale/version.h
    INSTALL    libswscale/libswscale.pc
    INSTALL    libavutil/adler32.h
    INSTALL    libavutil/aes.h
    INSTALL    libavutil/attributes.h
    INSTALL    libavutil/audio_fifo.h
    INSTALL    libavutil/audioconvert.h
    INSTALL    libavutil/avassert.h
    INSTALL    libavutil/avstring.h
    INSTALL    libavutil/avutil.h
    INSTALL    libavutil/base64.h
    INSTALL    libavutil/blowfish.h
    INSTALL    libavutil/bprint.h
    INSTALL    libavutil/bswap.h
    INSTALL    libavutil/buffer.h
    INSTALL    libavutil/channel_layout.h
    INSTALL    libavutil/common.h
    INSTALL    libavutil/cpu.h
    INSTALL    libavutil/crc.h
    INSTALL    libavutil/error.h
    INSTALL    libavutil/eval.h
    INSTALL    libavutil/fifo.h
    INSTALL    libavutil/file.h
    INSTALL    libavutil/frame.h
    INSTALL    libavutil/hmac.h
    INSTALL    libavutil/imgutils.h
    INSTALL    libavutil/intfloat.h
    INSTALL    libavutil/intfloat_readwrite.h
    INSTALL    libavutil/intreadwrite.h
    INSTALL    libavutil/lfg.h
    INSTALL    libavutil/log.h
    INSTALL    libavutil/mathematics.h
    INSTALL    libavutil/md5.h
    INSTALL    libavutil/mem.h
    INSTALL    libavutil/murmur3.h
    INSTALL    libavutil/dict.h
    INSTALL    libavutil/old_pix_fmts.h
    INSTALL    libavutil/opt.h
    INSTALL    libavutil/parseutils.h
    INSTALL    libavutil/pixdesc.h
    INSTALL    libavutil/pixfmt.h
    INSTALL    libavutil/random_seed.h
    INSTALL    libavutil/rational.h
    INSTALL    libavutil/ripemd.h
    INSTALL    libavutil/samplefmt.h
    INSTALL    libavutil/sha.h
    INSTALL    libavutil/sha512.h
    INSTALL    libavutil/time.h
    INSTALL    libavutil/timecode.h
    INSTALL    libavutil/timestamp.h
    INSTALL    libavutil/version.h
    INSTALL    libavutil/xtea.h
    INSTALL    libavutil/lzo.h
    INSTALL    libavutil/avconfig.h
    INSTALL    libavutil/libavutil.pc
    
     link ffmpeg.

    二、新建一个Android工程,在工程目录下新建一个jni文件夹,在文件夹下新建一个ffmpeg文件夹,用来放ffmpeg相关的头文件。在ffmpeg文件夹下新建Android.mk文件用来预先加载ffmpeg动态库。Android.mk文件内容如下:

     

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := ffmpeg
    LOCAL_SRC_FILES := /path/to/build/output/libffmpeg.so
    include $(PREBUILT_SHARED_LIBRARY)

    三、在jni下新建Android.mk文件和Application.mk两个文件用来指定编译的顺序和编译的平台以及对应的cpu指令集。

    Application.mk
    
    APP_ABI := armeabi
    APP_PLATFORM := android-9
    Android.mk

    include $(call all-subdir-makefiles)

    四、编写JNI文件,用来绑定java文件与.c文件的交互,文件内容如下:

    /*
     * ffmpeg_jni.c
     *
     *  Created on: Sep 1, 2014
     *      Author: clarck
     */
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <jni.h>
    
    #include "../include/ffmpeg_logger.h"
    #include "../include/ffmpeg.h"
    
    // 指定要注册的类,对应完整的java类名
    #define JNIREG_CLASS "com/clarck/android/ffmpeg/MainActivity"
    
    JNIEXPORT void JNICALL native_setDataSource(JNIEnv *env, jclass classzz, jstring path) {
        char *filepath = ffmpeg_jstringTostr(env, path);
        ffmpeg_setDataSource(filepath);
    }
    
    //Java和JNI函数的绑定
    static JNINativeMethod method_table[] = {
        { "setDataSource", "(Ljava/lang/String;)V", native_setDataSource }
    };
    
    //注冊native方法到java中
    static int registerNativeMethods(JNIEnv *env, const char *className,
            JNINativeMethod *gMethods, int numMethods) {
        jclass clazz;
        clazz = (*env)->FindClass(env, className);
        if (clazz == NULL) {
            return JNI_FALSE;
        }
    
        if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
            return JNI_FALSE;
        }
    
        return JNI_TRUE;
    }
    
    //調用註冊方法
    int register_ndk_load(JNIEnv *env) {
        return registerNativeMethods(env, JNIREG_CLASS, method_table,
                (int) (sizeof(method_table) / sizeof(method_table[0])));
    }
    
    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
        JNIEnv *env = NULL;
        jint result = -1;
    
        if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
            return result;
        }
    
        register_ndk_load(env);
    
        //返回JNI的版本
        return JNI_VERSION_1_6;
    }

    五、编写ffmpeg调用函数,内容如下:

    /*
     * ffmpeg.c
     *
     *  Created on: Sep 1, 2014
     *      Author: clarck
     */
    #include <jni.h>
    #include <android/native_window_jni.h>
    #include "../include/ffmpeg.h"
    #include "../include/ffmpeg_logger.h"
    #include "../ffmpeg/include/libavcodec/avcodec.h"
    #include "../ffmpeg/include/libavformat/avformat.h"
    #include "../ffmpeg/include/libavutil/pixfmt.h"
    #include "../ffmpeg/include/libswscale/swscale.h"
    
    char* ffmpeg_jstringTostr(JNIEnv* env, jstring jstr) {
        char* pStr = NULL;
    
        jclass jstrObj = (*env)->FindClass(env, "java/lang/String");
        jstring encode = (*env)->NewStringUTF(env, "utf-8");
        jmethodID methodId = (*env)->GetMethodID(env, jstrObj, "getBytes",
                "(Ljava/lang/String;)[B");
        jbyteArray byteArray = (jbyteArray) (*env)->CallObjectMethod(env, jstr,
                methodId, encode);
        jsize strLen = (*env)->GetArrayLength(env, byteArray);
        jbyte *jBuf = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);
    
        if (jBuf > 0) {
            pStr = (char*) malloc(strLen + 1);
    
            if (!pStr) {
                return NULL ;
            }
    
            memcpy(pStr, jBuf, strLen);
    
            pStr[strLen] = 0;
        }
    
        (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, 0);
    
        return pStr;
    }
    
    void ffmpeg_setDataSource(char *file_path) {
        LOGI("ffmpeg_setDataSource:%s", file_path);
    
        AVFormatContext *pFormatCtx;
        AVCodecContext *pCodecCtx;
        AVCodec *pCodec;
        AVFrame *pFrame, *pFrameYUV;
        AVPacket *packet;
        uint8_t *out_buffer;
    
        static struct SwsContext *img_convert_ctx;
    
        int videoStream, i, numBytes;
        int ret, got_picture;
    
        av_register_all();
        pFormatCtx = avformat_alloc_context();
    
        if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) {
            LOGE("can't open the file. 
    ");
            return;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            LOGE("Could't find stream infomation.
    ");
            return;
        }
    
        videoStream = 1;
        for (i = 0; i < pFormatCtx->nb_streams; i++) {
            if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                videoStream = i;
            }
        }
    
        if (videoStream == -1) {
            LOGE("Didn't find a video stream.
    ");
            return;
        }
    
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    
        if (pCodec == NULL) {
            LOGE("Codec not found.
    ");
            return;
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            LOGE("Could not open codec.
    ");
            return;
        }
    
        pFrame = av_frame_alloc();
        pFrameYUV = av_frame_alloc();
    
        numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
                pCodecCtx->height);
        out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
        avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
                pCodecCtx->width, pCodecCtx->height);
    
        int y_size = pCodecCtx->width * pCodecCtx->height;
    
        packet = (AVPacket *) malloc(sizeof(AVPacket));
        av_new_packet(packet, y_size);
    
        av_dump_format(pFormatCtx, 0, file_path, 0);
    
        while (av_read_frame(pFormatCtx, packet) >= 0) {
            if (packet->stream_index == videoStream) {
                ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
                        packet);
    
                LOGI("avcodec_decode_video2 ret:%d", ret);
    
                if (ret < 0) {
                    LOGE("decode error.
    ");
                    return;
                }
    
                if (got_picture) {
                    //TODO 此处可以将解码出来的图片保存起来。
                }
            }
            av_free_packet(packet);
        }
    
        av_free(out_buffer);
        av_free(pFrameYUV);
        avcodec_close(pCodecCtx);
        avformat_close_input(&pFormatCtx);
    }

    六、编写Android.mk用来编译相关的.c文件,内容如下:

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    FFMPEG_PATH := ../ffmpeg
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(FFMPEG_PATH)/include
    
    LOCAL_MODULE    := ffmpeg_player
    LOCAL_SRC_FILES += ffmpeg_jni.c 
    LOCAL_SRC_FILES += ffmpeg.c
    
    LOCAL_SHARED_LIBRARIES := ffmpeg
    LOCAL_LDLIBS := -llog
    
    include $(BUILD_SHARED_LIBRARY)

    七、编写java文件中相关执行调用方法

    package com.clarck.android.ffmpeg;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            setDataSource("/sdcard/a.mp4");
        }
    
        public native void setDataSource(String path);
        
        static {
            System.loadLibrary("ffmpeg");
            System.loadLibrary("ffmpeg_player");
        }
    }

    八、执行结果如下图:

  • 相关阅读:
    mysql中的enum型
    mysql中的时间year/date/time/datetime
    一些数字的属性
    mysql增删
    Perl6 Bailador框架(8):自定义400/500
    react: typescript jest && enzyme
    webstorm tslint配置
    react: typescript import images alias
    JSONP原理及简单实现
    纯css画三角形
  • 原文地址:https://www.cnblogs.com/tanlon/p/3950090.html
Copyright © 2011-2022 走看看