(一).目的:在Android Studio上调用FFmpeg,播放视频,rtsp流

(二).准备工作:ffmpeg库下载    ndk下载 (我用的是 android-ndk-r20b   Ffmpeg 3.0)

(三).1.环境变量中配置好ndk路径  ,如下图:

          2.Android Studio中配置好ndk路径,如下图:

      ----------------------------

 

(四).调用FFmpeg的整体调用逻辑为:
             
1 编译完ffmpeg库(我直接网上拷贝下来编译好的)
              2 使用jni方式撰写c代码,其中需要包含相应的ffmpeg的头文件
              3 撰写相应的Android.mk文件,里面指定需要编译的c代码以及需要链接的动态库
              4 执行ndk-build生成相应的jni库
              5 创建andorid java程序,代码中loadLibrary相应ffmpeg库以及刚才生成的jni库
              6 静态方法声明native函数,这些函数在jni写的c语言中都已经实现过

我的操作顺序和上面的排序有些不一样,但是每一步都是不能缺少的,我只是顺序不同而已,最终不影响运行

1.操作如下:在main下新建jni文件夹

2.加入编译好的库和自定义的方法(我自己创建了一个类MyNdk)

注意点:play是视频播放的方法,别的方法可以不要,对应的test.c里面也只留play的方法

3.生成com_example_im_myapplication_MyNdk.h 文件

javah -encoding UTF-8 -classpath C:\Users\IM\AppData\Local\Android\Sdk\platforms\android-25\android.jar;. -jni com.example.im.ffmpegtest3.MainActivity(自己项目的路径)

4.在Jni 包下面加入已经编译好的 ffmpeg库中的Inclued 和 lib 文件夹,直接复制进来

5.加入 Android.mk    Application.mk   test.c

a:Android.mk 文件代码如下:

LOCAL_PATH := $(call my-dir)# FFmpeg libraryinclude $(CLEAR_VARS)LOCAL_MODULE := avcodecLOCAL_SRC_FILES := libavcodec-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avfilterLOCAL_SRC_FILES := libavfilter-6.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avformatLOCAL_SRC_FILES := libavformat-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avutilLOCAL_SRC_FILES := libavutil-55.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := swresampleLOCAL_SRC_FILES := libswresample-2.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := swscaleLOCAL_SRC_FILES := libswscale-4.soinclude $(PREBUILT_SHARED_LIBRARY)# Programinclude $(CLEAR_VARS)LOCAL_MODULE := MyApplication(名字要对应)LOCAL_SRC_FILES := test.c   (名字要对应)LOCAL_C_INCLUDES += $(LOCAL_PATH)/includeLOCAL_LDLIBS := -llog -lz -landroidLOCAL_SHARED_LIBRARIES := avcodec avfilter avformat avutil swresample swscaleinclude $(BUILD_SHARED_LIBRARY)

b: Application.mk代码如下:

APP_ABI := armeabi-v7aAPP_MODULES := MyApplication(名字要对应)

c:test.c代码如下:方法的名字格式要注意一下,最容易出错了

#include #include "com_example_im_myapplication_MyNdk.h"#include "include/libavformat/avformat.h"#include "include/libavcodec/avcodec.h"#include "include/libavutil/avutil.h"#include "include/libavfilter/avfilter.h"#include #include #include #include #define LOG_TAG "JNI"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)#define SWS_BICUBIC           4#define SWS_BILINEAR           2/** * com.ihubin.ffmpegstudy.MainActivity.avformatinfo() * AVFormat Support Information */JNIEXPORT jstring Java_com_example_im_myapplication_MyNdk_avformatinfo(JNIEnv *env, jobject obj){    char info[40000] = { 0 };    av_register_all();    AVInputFormat *if_temp = av_iformat_next(NULL);    AVOutputFormat *of_temp = av_oformat_next(NULL);    //Input    while(if_temp!=NULL){        sprintf(info, "%s[In ][%10s]\n", info, if_temp->name);        if_temp=if_temp->next;    }    //Output    while (of_temp != NULL){        sprintf(info, "%s[Out][%10s]\n", info, of_temp->name);        of_temp = of_temp->next;    }    //LOGE("%s", info);    return (*env)->NewStringUTF(env, info);}/** * com.ihubin.ffmpegstudy.MainActivity.avcodecinfo() * AVCodec Support Information */JNIEXPORT jstring JNICALL Java_com_example_im_myapplication_MyNdk_avcodecinfo(JNIEnv *env, jobject obj){    char info[40000] = { 0 };    av_register_all();    AVCodec *c_temp = av_codec_next(NULL);    while(c_temp!=NULL){        if (c_temp->decode!=NULL){            sprintf(info, "%s[Dec]", info);        }        else{            sprintf(info, "%s[Enc]", info);        }        switch (c_temp->type){        case AVMEDIA_TYPE_VIDEO:            sprintf(info, "%s[Video]", info);            break;        case AVMEDIA_TYPE_AUDIO:            sprintf(info, "%s[Audio]", info);            break;        default:            sprintf(info, "%s[Other]", info);            break;        }        sprintf(info, "%s[%10s]\n", info, c_temp->name);        c_temp=c_temp->next;    }    //LOGE("%s", info);    return (*env)->NewStringUTF(env, info);}/** * com.ihubin.ffmpegstudy.MainActivity.avfilterinfo() * AVFilter Support Information */JNIEXPORT jstring JNICALL Java_com_example_im_myapplication_MyNdk_avfilterinfo(JNIEnv *env, jobject obj){    char info[40000] = { 0 };    avfilter_register_all();    AVFilter *f_temp = (AVFilter *)avfilter_next(NULL);    int i = 0;    while (f_temp != NULL){        sprintf(info, "%s[%10s]\n", info, f_temp->name);        f_temp = f_temp->next;    }    return (*env)->NewStringUTF(env, info);}/** * com.ihubin.ffmpegstudy.MainActivity.urlprotocolinfo() * Protocol Support Information */JNIEXPORT jstring JNICALL Java_com_example_im_ffmpegtest3_MainActivity_configurationinfo(JNIEnv *env, jobject obj){    char info[10000] = {0};    av_register_all();    sprintf(info, "%s\n", avcodec_configuration());    //LOGE("%s", info);    return (*env)->NewStringUTF(env, info);}//2020-12-24wppstatic AVPacket *vPacket;static AVFrame *vFrame, *pFrameRGBA;static AVCodecContext *vCodecCtx;struct SwsContext *img_convert_ctx;static AVFormatContext *pFormatCtx;ANativeWindow* nativeWindow;ANativeWindow_Buffer windowBuffer;uint8_t *v_out_buffer;JNIEXPORT void JNICALL Java_com_example_im_myapplication_MyNdk_play(JNIEnv *env, jclass clazz, jstring url, jobject surface) {    //char input_str[500]={0};    //sprintf(input_str, "%s", (*env)->GetStringUTFChars(env,url,NULL));   // LOGI("%s",input_str);      char file_name[500]={0};      //读取输入的视频频文件地址      sprintf(file_name, "%s", (*env)->GetStringUTFChars(env,url, NULL));      //LOGI("%s",file_name);      // char * file_name = "/storage/emulated/999/tencent/MicroMsg/WeiXin/mmexport1588601045658.mp4";       av_register_all();        AVFormatContext * pFormatCtx = avformat_alloc_context();        //AVDictionary* options = NULL;        //av_dict_set(&options, "rtsp_transport", "udp", 0);        // Open video file        if(avformat_open_input(&pFormatCtx, file_name, NULL, NULL)!=0) {        LOGE("Couldn't open file:%s\n", file_name);        return; // Couldn't open file        }        // Retrieve stream information        if(avformat_find_stream_info(pFormatCtx, NULL)<0) {        LOGE("Couldn't find stream information.");        return;        }        // Find the first video stream        int videoStream = -1,i;        for (i = 0; i < pFormatCtx->nb_streams; i++) {         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO            && videoStream < 0) {             videoStream = i;         }        }        if(videoStream==-1) {         LOGE("Didn't find a video stream.");         return; // Didn't find a video stream        }        // Get a pointer to the codec context for the video stream        AVCodecContext  * pCodecCtx = pFormatCtx->streams[videoStream]->codec;        // Find the decoder for the video stream        AVCodec * pCodec = avcodec_find_decoder(pCodecCtx->codec_id);        if(pCodec==NULL) {         LOGE("Codec not found.");         return; // Codec not found        }        if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {         LOGE("Could not open codec.");         return; // Could not open codec        }        // 获取native window        ANativeWindow* nativeWindow = ANativeWindow_fromSurface(env, surface);        // 获取视频宽高        int videoWidth = pCodecCtx->width;        int videoHeight = pCodecCtx->height;        // 设置native window的buffer大小,可自动拉伸        ANativeWindow_setBuffersGeometry(nativeWindow,  videoWidth, videoHeight, WINDOW_FORMAT_RGBA_8888);        ANativeWindow_Buffer windowBuffer;        if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) {         LOGE("Could not open codec.");         return; // Could not open codec         }        // Allocate video frame        AVFrame * pFrame = av_frame_alloc();        // 用于渲染        AVFrame * pFrameRGBA = av_frame_alloc();        if(pFrameRGBA == NULL || pFrame == NULL) {            LOGE("Could not allocate video frame.");            return;        }        // Determine required buffer size and allocate buffer            int numBytes=av_image_get_buffer_size(AV_PIX_FMT_RGBA, pCodecCtx->width, pCodecCtx->height, 1);            uint8_t * buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));            av_image_fill_arrays(pFrameRGBA->data, pFrameRGBA->linesize, buffer, AV_PIX_FMT_RGBA,                                 pCodecCtx->width, pCodecCtx->height, 1);        // 由于解码出来的帧格式不是RGBA的,在渲染之前需要进行格式转换        struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width,                             pCodecCtx->height,                             pCodecCtx->pix_fmt,                             pCodecCtx->width,                             pCodecCtx->height,                             AV_PIX_FMT_RGBA,                             SWS_BILINEAR,                             NULL,                             NULL,                             NULL);        int frameFinished;        AVPacket packet;        while(av_read_frame(pFormatCtx, &packet)>=0) {        // Is this a packet from the video stream?        if(packet.stream_index==videoStream) {            // Decode video frame            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);            // 并不是decode一次就可解码出一帧            if (frameFinished) {                // lock native window buffer                ANativeWindow_lock(nativeWindow, &windowBuffer, 0);                // 格式转换                sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,                          pFrame->linesize, 0, pCodecCtx->height,                          pFrameRGBA->data, pFrameRGBA->linesize);                // 获取stride                uint8_t * dst = windowBuffer.bits;                int dstStride = windowBuffer.stride * 4;                uint8_t * src = (uint8_t*) (pFrameRGBA->data[0]);                int srcStride = pFrameRGBA->linesize[0];                // 由于window的stride和帧的stride不同,因此需要逐行复制                int h;                // LOGI("%d",videoHeight);                for (h = 0; h < videoHeight; h++) {                    memcpy(dst + h * dstStride, src + h * srcStride, srcStride);                }                //LOGI("%s","111111111111");                ANativeWindow_unlockAndPost(nativeWindow);            }        }        av_packet_unref(&packet);    }     //内存释放    ANativeWindow_release(nativeWindow);    av_free(buffer);    av_free(pFrameRGBA);    // Free the YUV frame    av_free(pFrame);    // Close the codecs    avcodec_close(pCodecCtx);    // Close the video file    avformat_close_input(&pFormatCtx);    //return 0;}注意点:①include 文件名称别写错了   ② 方法的名字格式不要错了      ③Java_com_example_im_myapplication_MyNdk_play是视频播放的方法,别的方法可以不要

 

6.ndk-build,先进入到jni的目录下

有时候会提示不存在该命令:那么先set path一下   set path=D:\software\android-ndk-r20b\build    然后再ndk-buil(根据自己的地址来)

6.现在只要在界面上显示出来就可以了

7.运行成功:

研究了好久,终于研究出来了,开心,为自己鼓个掌 

 

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. Android之判断是否有网封装类
  5. android 读 txt
  6. Android(安卓)SDK下载和更新失败的解决方法
  7. Android线程之消息机制(Handler、MessageQueue、Looper、Thread)
  8. Android编译系统(Android.mk文件详解-仅供参考)
  9. Ubuntu下编译AOSP步骤

随机推荐

  1. 获取当前应用的版本号/应用名称
  2. android用听筒不断播放音乐
  3. android ViewPager加Fragment实现导航加
  4. android source code compile
  5. Android之Message机制的灵活应用
  6. 深入浅出 - Android系统移植与平台开发(一
  7. Android 实现视频录制并播放
  8. 2.4.6 星级评分条
  9. Android学习札记8:对TextView中的文字中间
  10. Android获取屏幕分辨率 dp pix转换