Android FFmpeg02 --- 播放本地视频

2022-04-14  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读