zoukankan      html  css  js  c++  java
  • Android 音视频深入 六 使用FFmpeg播放视频(附源码下载)

    本篇项目地址,求star
    https://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91

    首先FFmpeg是c语言写的,所以我们需要NDK的技术,然后我使用的NDK使用Cmake的,一开始就是说如何将FFmpeg导入项目,使用我的方法导入FFmpeg不用一分钟。

    这个需要大家先在上面的代码地址里下载项目代码
    因为FFmpeg这个基于android的so文件如何生成的我不写出来,我也是直接用别人文件,直接使用我项目里的就好了

    1.FFmpeg简单的说明


    多媒体视频处理工具FFmpeg有非常强大的功能包括视频采集功能、视频格式转换、视频抓图、给视频加水印等。

    他的功能有7大部分完整

    libavcodec:提供范围更广的编解码器的实现。

    libavformat:实现流媒体协议,容器格式和基本的I/O访问。

    libavutil:包括校验,解压缩和各种实用功能。

    libavfilter:提供了一个平均改变解码音频和视频通过过滤器链。

    libavdevice:提供抽象访问捕获和重放设备。

    libswresample:实现音频混合和重采样程序。

    libswscale:实现颜色转换和缩放程序。

    2.环境配置


    将下载的项目里jniLibs和cpp粘贴到自己创建的项目的main文件夹下

    我还需要在app module的build.gradle添加代码,在defaultConfig里添加ndk支持的类型,还有给Cmake添加参数,在android下导入CMakeLists文件,例子代码如下:

    android {
        compileSdkVersion 26
        defaultConfig {
            applicationId "jonesx.videoplayer"
            minSdkVersion 19
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            ndk {
                abiFilters 'armeabi'
            }
            externalNativeBuild {
                cmake {
                    arguments '-DANDROID_TOOLCHAIN=clang','-DANDROID_STL=gnustl_static'
                }
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        externalNativeBuild {
            cmake {
                path "src/main/cpp/CMakeLists.txt"
            }
        }
    }

    3.代码说明

    首先就是能够使用cpp文件夹下的VideoPlayer的代码,那我们就需要创建一个VideoPlayer的java类

    public class VideoPlayer {
    
        static {
            System.loadLibrary("VideoPlayer");
        }
    
        public static native int play(Object surface);
    }

    使用这个play函数,直接在SurfaceView的surfaceCreated函数里开启线程使用

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    VideoPlayer.play(surfaceHolder.getSurface());
                }
            }).start();
        }

    那重点来了,说一说VideoPlayer用到了FFmpeg哪些东西

    获取视频格式的环境,打开MP4文件

    AVFormatContext *pFormatCtx = avformat_alloc_context();
    
        if (avformat_open_input(&pFormatCtx, file_name, NULL, NULL) != 0) {
    
            LOGD("Couldn't open file:%s
    ", file_name);
            return -1; // Couldn't open file
        }

    查看是否有流,如果那就看是否有视频流

        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            LOGD("Couldn't find stream information.");
            return -1;
        }
    
        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) {
            LOGD("Didn't find a video stream.");
            return -1; // Didn't find a 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) {
            LOGD("Codec not found.");
            return -1; // Codec not found
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            LOGD("Could not open codec.");
            return -1; // Could not open codec
        }

    通过surface获取目前手机屏幕给这个Surface的内存空间

        // 获取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) {
            LOGD("Could not open codec.");
            return -1; // Could not open codec
        }

    转格式
     

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



    首先这个解码是在一个循环里,然后解码,和之前一样一帧一帧的解码,但是如果一帧太大那就下一次循环里继续解码

    avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

    释放资源

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

    完了,说是完了,这只是开始,我对FFmpeg的学习也是开始,以后我可能断断续续的分享我使用FFmpeg的心得。

    博客首发地址
    http://blog.csdn.net/z979451341 

  • 相关阅读:
    Splay专题总结
    UVa12657
    ZOJ3772
    POJ1743
    高斯消元模板
    python使用chrom登陆微博
    mysql常用数据库(表)命令
    mysql索引
    mysql建表的时候,时间戳的选用
    php 金额每三位添加一个逗号
  • 原文地址:https://www.cnblogs.com/jianpanwuzhe/p/8421220.html
Copyright © 2011-2022 走看看