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对视频文件的解封装的操作