Android FFmpeg视频播放器一解封装

1.导入编译好的FFmpeg库文件以及头文件

笔者使用的是静态库,静态库存放路径是:src/main/cpp/ffmpeg/libs/armeabi-v7a

导入的静态库:

  • libavcodec.a
  • libavfilter.a
  • libavformat.a
  • libavutil.a
  • libswresample.a
  • libswscale.a

头文件路径:src/main/cpp/ffmpeg/include

2.配置CMakeLists.txt配置文件

cmake_minimum_required(VERSION 3.18.1)

project("msplayer")

# ffmpeg的路径
set(FFMPEG ${CMAKE_SOURCE_DIR}/ffmpeg)
# rtmp的路径
set(RTMP ${CMAKE_SOURCE_DIR}/rtmp)
# 导入ffmpeg的头文件
include_directories(${FFMPEG}/include)


#ffmpeg库指定
set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -L${FFMPEG}/libs/${CMAKE_ANDROID_ARCH_ABI}")
#rtmp库指定
set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -L${RTMP}/libs/${CMAKE_ANDROID_ARCH_ABI}")

# 批量导入 源文件
file(GLOB src_files *.cpp)


add_library(
        msplayer
        SHARED
        ${src_files})


find_library(
        log-lib
        log)


target_link_libraries(
        msplayer
        # 忽略顺序的方式,导入
        -Wl,--start-group
        avcodec avfilter avformat avutil swresample swscale
        -Wl,--end-group
        z   #libz.so 是FFMpeg 额外需要的库文件
        rtmp
        android  #ANativeWindow 用来渲染画面
        OpenSLES  #OpenSLES 用来播放PCM格式的音频

        ${log-lib})

3.视频解封装

void MSPlayer::prepare_() {
  
    /*avFormatContext 视频格式上下文 */
    avFormatContext = avformat_alloc_context();

    /*这个参数用于设置额外信息*/
    AVDictionary *dictionary = nullptr;
    av_dict_set(&dictionary, "timeout", "5000000", 0);// 单位微妙
	
    /**data_source 视频文件绝对地址 或者rtmp推流地址*/
    int r = avformat_open_input(&avFormatContext, data_source, nullptr, &dictionary);
    /*释放字典,用完必须释放*/
    av_dict_free(&dictionary);
    if (r) {
        if (helper) {
            /**视频地址或者url异常*/
            helper->onError(MS_THREAD_CHILD, FFMPEG_CAN_NOT_OPEN_URL);
        }
        avformat_close_input(&avFormatContext);
        return;
    }

     /*
      * 查找媒体中的音视频流的信息
      */
    r = avformat_find_stream_info(avFormatContext, nullptr);
    if (r < 0) {
        if (helper) {  
               /**未发现音视频流*/
            helper->onError(MS_THREAD_CHILD, FFMPEG_CAN_NOT_FIND_STREAMS);
        }
        avformat_close_input(&avFormatContext);
        return;
    }

    /**得到视频长度*/
    this->duration = avFormatContext->duration / AV_TIME_BASE;

    /*编解码上下文*/
    AVCodecContext *codecContext = nullptr;

    /*遍历视频流:一个音频流一个视频流*/
    for (int i = 0; i < avFormatContext->nb_streams; ++i) {
        /*获取媒体流(视频 or 音频)*/
        AVStream *stream = avFormatContext->streams[i];

        /*
         *  从流中获取参数(宽高 等等)后面的解码器需要用到
         *  */
        AVCodecParameters *parameters = stream->codecpar;
        
        /*可以拿到视频宽高*/
        int width = parameters->width;
        int height = parameters->height;

        /*得到编解码器*/
        AVCodec *codec = avcodec_find_decoder(parameters->codec_id);
        if (!codec) {
            if (helper) {
                helper->onError(MS_THREAD_CHILD, FFMPEG_FIND_DECODER_FAIL);
            }
            avformat_close_input(&avFormatContext);
            return;
        }

        /*得到解码器上下文*/
        codecContext = avcodec_alloc_context3(codec);
        if (!codecContext) {
            if (helper) {
                /**得到解码器失败*/
                helper->onError(MS_THREAD_CHILD, FFMPEG_FIND_DECODER_FAIL);
            }
            /*释放此上下文codecContext就行,codec内部会自动释放 */
            avcodec_free_context(&codecContext);
            avformat_close_input(&avFormatContext);
            return;
        }

        /**
        *  将parameters 初始化codecContext
         */
        r = avcodec_parameters_to_context(codecContext, parameters);
        if (r < 0) {
            if (helper) {
                /*初始化codecContext 失败*/
                helper->onError(MS_THREAD_CHILD, FFMPEG_ALLOC_CODEC_CONTEXT_FAIL);
            }
            /*释放此上下文 codecContext就行,codec内部会自动释放 */
            avcodec_free_context(&codecContext);
            avformat_close_input(&avFormatContext);
            return;
        }

        /*打开解码器*/
        r = avcodec_open2(codecContext, codec, nullptr);
        if (r) {
            if (helper) {
                /*打开解码器失败*/
                helper->onError(MS_THREAD_CHILD, FFMPEG_OPEN_DECODER_FAIL);
            }
            avcodec_free_context(&codecContext);
            avformat_close_input(&avFormatContext);
            return;
        }

        AVRational time_base = stream->time_base;

         /*通过流类型创建对应的通道进行进行解码操作*/
        if (parameters->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
            /*初始化音频通道对象*/
            audio_channel = new AudioChannel(i, codecContext, time_base);
        } else if (parameters->codec_type == AVMediaType::AVMEDIA_TYPE_VIDEO) {

            /*视频独有的fps值*/
            AVRational fps_rational = stream->avg_frame_rate;
            int fps = av_q2d(fps_rational);
            /*初始化视频通道*/
            video_channel = new VideoChannel(i, codecContext, time_base, fps);

        }

    }

    if (!audio_channel && !video_channel) {
        if (helper) {
            helper->onError(MS_THREAD_CHILD, FFMPEG_NOMEDIA);
        }
        if (codecContext) {
            /**释放此上下文 codecContext 不用管codec*/
            avcodec_free_context(&codecContext);
        }
        avformat_close_input(&avFormatContext);
        return;
    }

    /*prepare success*/
    if (helper) {
        helper->onPrepared(MS_THREAD_CHILD);
    }
}

解封状流程:

  • avformat_alloc_context() 创建avFormatContext 视频格式上下文
  • avformat_open_input() 打开视频资源
  • avformat_find_stream_info() 查找视频中的流信息
  • avFormatContext->streams[i] 得到音频或者视频流信息
  • stream->codecpar 从流中得到解码器的参数信息(也就是视频参数信息)
  • avcodec_find_decoder 通过参数中的codec_id 找到解码器AVCodec
  • avcodec_alloc_context3(codec) 得到解码器上下文 AVCodecContext
  • avcodec_parameters_to_context 初始化解码器上下文
  • avcodec_open2 打开解码器

上面就完成了FFmpeg对视频文件的解封装的操作

猜你喜欢

转载自blog.csdn.net/u014078003/article/details/125276827