美文网首页
Android FFmpeg02 --- 播放本地视频

Android FFmpeg02 --- 播放本地视频

作者: 沪漂意哥哥 | 来源:发表于2022-04-14 07:34 被阅读0次

    一. MainActivity

    public class MainActivity extends AppCompatActivity {
        Surface surface;
        static {
            System.loadLibrary("native-lib");
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            checkPermission();
            SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface);
            final SurfaceHolder surfaceViewHolder = surfaceView.getHolder();
    
            surfaceViewHolder.addCallback(new SurfaceHolder.Callback() {
                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                    surface = surfaceViewHolder.getSurface();
                }
                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {    }
                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {    }
            });
        }
    
        public boolean checkPermission() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{
                        Manifest.permission.READ_EXTERNAL_STORAGE,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.CAMERA
                }, 1);
    
            }
            return false;
        }
    
        public void play(View view) {
            String folderurl = new File(Environment.getExternalStorageDirectory(), "input.mp4").getAbsolutePath();
            play(folderurl, surface);
        }
    
        public native int play(String url, Surface surface);
    }
    
    

    二. build.gradle

    android {
        compileSdkVersion 28
        buildToolsVersion "28.0.3"
        defaultConfig {
            applicationId "com.luisliuyi.demo.ffmpeg"
            minSdkVersion 16
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            externalNativeBuild {
                cmake {
                    abiFilters 'armeabi-v7a'
                }
            }
            ndk{
                abiFilters 'armeabi-v7a'
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    
        sourceSets {
            main {
                jniLibs.srcDirs = ['src/main/cpp/libs']
            }
        }
    
        externalNativeBuild {
            cmake {
                path "src/main/cpp/CMakeLists.txt"
                version "3.10.2"
            }
        }
    }
    

    三.CMakeLists.txt

    cmake_minimum_required(VERSION 3.4.1)
    
    include_directories(include)
    
    #添加系统环境变量
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")
    
    
    add_library(
            native-lib
            SHARED
            native-lib.cpp)
    
    find_library(
            log-lib
            log)
    
    target_link_libraries(
            native-lib
            avcodec
            avdevice
            avfilter
            avformat
            avutil
            swresample
            swscale
            android
            ${log-lib})
    

    四.native-lib.cpp

    #include <jni.h>
    #include <string>
    
    extern "C"
    {
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/imgutils.h"
    #include "libswscale/swscale.h"
    #include <libavutil/time.h>
    }
    #include <android/log.h>
    #include <android/native_window_jni.h>
    #define LOGD(...) __android_log_print(ANDROID_LOG_INFO,"liuyi",__VA_ARGS__)
    
    static AVFormatContext *avFormatContext;
    static AVCodecContext *avCodecContext;
    AVCodec *vCodec;
    ANativeWindow* nativeWindow;
    ANativeWindow_Buffer windowBuffer;
    static AVPacket *avPacket;
    static AVFrame *avFrame, *rgbFrame;
    uint8_t *outbuffer;
    struct SwsContext *swsContext;
    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_luisliuyi_demo_ffmpeg_MainActivity_play(JNIEnv *env, jobject thiz, jstring url_,
                                                     jobject surface) {
        const char *url = env->GetStringUTFChars(url_, 0);
    
        //注册所有的组件
        avcodec_register_all();
    
        avformat_network_init();
    
        //实例化了上下文
        avFormatContext=avformat_alloc_context();
    
        //打开文件
        if(avformat_open_input(&avFormatContext,url,NULL,NULL)!=0){
            LOGD("Couldn't open input stream.\n");
            return -1;
        }
        LOGD("打开视频成功.\n");
    
        //查找文件的流信息  mp4  失败  的视频
        if(avformat_find_stream_info(avFormatContext,NULL)<0){
            LOGD("Couldn't find stream information.\n");
            return -1;
        }
    
        //视频流索引
        int videoindex = -1;
        for(int i=0; i<avFormatContext->nb_streams; i++) {
            if (avFormatContext->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO) {
                videoindex = i;
                break;
            }
        }
    
        if(videoindex == -1){
            LOGD("Couldn't find a video stream.\n");
            return -1;
        }
        LOGD("找到了视频流\n");
        env->ReleaseStringUTFChars(url_, url);
    
        //解码器上下文
        avCodecContext=avFormatContext->streams[videoindex]->codec;
        vCodec=avcodec_find_decoder(avCodecContext->codec_id);
    
        //打开解码器
        if(avcodec_open2(avCodecContext, vCodec,NULL)<0){
            LOGD("Couldn't open codec.\n");
            return -1;
        }
        LOGD("打开了解码成功\n");
    
        //获取界面传下来的surface
        nativeWindow = ANativeWindow_fromSurface(env, surface);
        if (0 == nativeWindow){
            LOGD("Couldn't get native window from surface.\n");
            return -1;
        }
    
        avFrame = av_frame_alloc();
        avPacket = av_packet_alloc();
        rgbFrame = av_frame_alloc();
    
        int width = avCodecContext->width;
        int height = avCodecContext->height;
    
        int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height,1 );
    
        //实例化一个输入缓冲区
        outbuffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
    
        //缓冲区 设置给 rgbferame
        av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, outbuffer, AV_PIX_FMT_RGBA, width,
                             height, 1);
    
        //转换器
        swsContext = sws_getContext(width, height, avCodecContext->pix_fmt,
                                    width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL);
    
        if (0 > ANativeWindow_setBuffersGeometry(nativeWindow,width,height,WINDOW_FORMAT_RGBA_8888)){
            LOGD("Couldn't set buffers geometry.\n");
            ANativeWindow_release(nativeWindow);
            return -1;
        }
    
        while (av_read_frame(avFormatContext, avPacket)>=0) {
            if (avPacket->stream_index == videoindex) {
                int ret = avcodec_send_packet(avCodecContext, avPacket);
                if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF){
                    LOGD("解码出错");
                    return -1;
                }
                ret = avcodec_receive_frame(avCodecContext, avFrame);
                if (ret == AVERROR(EAGAIN)) {
                    continue;
                } else if (ret < 0) {
                    break;
                }
    
                sws_scale(swsContext, avFrame->data, avFrame->linesize, 0, avCodecContext->height,
                          rgbFrame->data, rgbFrame->linesize);
                if (ANativeWindow_lock(nativeWindow, &windowBuffer, NULL) < 0) {
                    LOGD("cannot lock window");
                } else {
                    uint8_t *dst = (uint8_t *) windowBuffer.bits;
                    for (int h = 0; h < height; h++) {
                        memcpy(dst + h * windowBuffer.stride * 4, outbuffer + h * rgbFrame->linesize[0],
                               rgbFrame->linesize[0]);
                    }
                    switch(avFrame->pict_type){
                        case AV_PICTURE_TYPE_I:
                            LOGD("I");
                            break;
                        case AV_PICTURE_TYPE_P:
                            LOGD("P");
                            break;
                        case AV_PICTURE_TYPE_B:
                            LOGD("B");
                            break;
                        default:
                            ;break;
                    }
                }
                av_usleep(1000 * 33);
                ANativeWindow_unlockAndPost(nativeWindow);
            }
        }
        avformat_free_context(avFormatContext);
        env->ReleaseStringUTFChars(url_, url);
        return -1;
    }
    

    五.代码地址

    https://gitee.com/luisliuyi/android-ffmpeg01.git
    

    相关文章

      网友评论

          本文标题:Android FFmpeg02 --- 播放本地视频

          本文链接:https://www.haomeiwen.com/subject/puuzsrtx.html