[Cmake-Android]音视频总结:
*
[Cmake-Android音视频]SDK,NDK基本介绍
<https://blog.csdn.net/adolph_lu/article/details/90315151>
*
[Cmake-Android音视频]NDK-r14b编译ffmpeg3.4支持neon,硬解码
<https://blog.csdn.net/adolph_lu/article/details/90384620>
*
[Cmake-Android音视频]创建支持ffmpeg3.4的项目
<https://blog.csdn.net/adolph_lu/article/details/90477243>
*
[Cmake-Android音视频]ffmpeg3.4实现解封装
<https://blog.csdn.net/adolph_lu/article/details/90708620>
*
[Cmake-Android音视频]ffmpeg3.4软硬解码和多线程解码
<https://blog.csdn.net/adolph_lu/article/details/91348324>
*
[Cmake-Android音视频]ffmpeg3.4视频格式转换和显示
<https://blog.csdn.net/adolph_lu/article/details/91997478>
*
[Cmake-Android音视频]ffmpeg3.4音频重采样
<https://blog.csdn.net/adolph_lu/article/details/92795081>
*
[Cmake-Android音视频]OpenSLES音频播放
<https://blog.csdn.net/adolph_lu/article/details/93349809>
1.解码流程图
2.函数介绍
avcodec_register_all()
注册解码器格式,比如h264,mjpeg。
avcodec_find_decoder(...)
通过解码器ID查找相应的解码器。如果没有找到,应该是在编译ffmpeg的时候没有打开相应的解码器。此时,需要在ffmpeg的configure文件中打开,并重新编译出库文件。
avcodec_find_decoder_by_name(...)
通过名字来查找解码器。
//硬解码 avcodec_find_decoder_by_name("h264_mediacodec");
avcodec_open2(...)
打开解码器
//可以通过options来设置多线程解码 //所有可设置的参数在/libavcodec/options_table.h //int
thread_count 解码线程数 //time_base 时间基数 int avcodec_open2(AVCodecContext *avctx,
const AVCodec*codec, AVDictionary **options)
avcodec_send_packet(...)
发送到线程中解码,packet会被复制一份,我们可以直接清理packet。
该函数会有缓存,发送到文件结尾后,可以通过发送NULL,将缓存数据取出。
avcodec_receive_frame(...)
从解码成功的数据中取出一个 frame, 解码的时候前几帧的可能获取失败,因为avcodec_send_packet(...)会有缓存,
导致播放的时候视频最后的几帧没有播放。
解决方案:
读到文件结尾处的时候,avcodec_send_packet()中的avpkt传NULL,然后在avcodec_receive_frame()一直读,,直到取不出帧为止。
接收已经解码好的数据。接收的数据和发送的数据并不一定是一一对应的。
3.关键结构体介绍
AVCodecContext
描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息。
//空间申请 AVCodecContext *avcodec_alloc_context3(const AVCodec *codec) //释放 void
avcodec_free_context(AVCodecContext **avctx); //把avstream中的参数复制到codec中
avcodec_parameters_to_context(codec, p); //关键参数 enum AVMediaType
codec_type:编解码器的类型(视频,音频...) struct AVCodec
*codec:采用的解码器AVCodec(H.264,MPEG2...) int bit_rate:平均比特率 uint8_t *extradata; int
extradata_size:针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等) AVRational
time_base:根据该参数,可以把PTS转化为实际的时间(单位为秒s) int width, height:如果是视频的话,代表宽和高 int
sample_rate:采样率(音频) int channels:声道数(音频) enum AVSampleFormat sample_fmt:采样格式
AVFrame
用于存放解码后的数据,对视频来说是YUV,RGB,对音频来说是PCM
//分配内存空间 AVFrame *frame = av_frame_alloc() //释放内存空间 void av_frame_free(AVFrame
**frame) //关键参数 //解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM) uint8_t
*data[AV_NUM_DATA_POINTERS]; //视频一行的数据大小 音频一个通道数据大小 存放的目的主要是为了按字节对齐 int
linesize[AV_NUM_DATA_POINTERS]; //视频宽高 int width, height; //音频单通道样本数 待定 int
nb_samples; //收到的当前帧的pts int64_t pts; //packet中的dts int64_t pkt_dts; //音频采样率
通道类型 通道数 int sample_rate;uint64_t channel_layout;int channels; //视频---图片格式
音频---采样格式 int format; //AVPixelFormat AVSampleFormat
4.关键代码
多线程neon软解码关键代码 neon是在编译fffmpeg的时候配置的
//初始化解码器 avcodec_register_all(); //软解码器 AVCodec *codec =
avcodec_find_decoder(vStream->codecpar->codec_id); if (!codec) {
LOGI("avcodec_find_decoder failed"); return env->NewStringUTF(hello.c_str()); }
//初始化解码器上下文 AVCodecContext *cc = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(cc, vStream->codecpar); cc->thread_count = 8;
//解码线程数 //打开解码器 re = avcodec_open2(cc, 0, 0); if (re != 0) {
LOGI("avcodec_open2 failed! %s", av_err2str(re)); return
env->NewStringUTF(hello.c_str()); } LOGI("avcodec_open2 success"); //读取帧数据
AVPacket *pkt = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); for (; ;
) { int re = av_read_frame(ic, pkt); if (re != 0) { LOGI("读到结尾处了"); //传递NULL
保证缓存数据也能被取出 avcodec_send_packet(cc, NULL); //int pos = 20 *
r2d(vStream->time_base); //av_seek_frame(ic, videoStream, pos,
AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME); continue; } //只测试视频 if
(pkt->stream_index != videoStream) { continue; } //发送到线程中解码 re =
avcodec_send_packet(cc, pkt); //清理 av_packet_unref(pkt); if (re != 0) {
LOGI("avcodec_send_packet FAILED"); continue; } //保证能接受到所有的数据 for (;;) { re =
avcodec_receive_frame(cc, frame); if (re != 0) { // LOGI("avcodec_receive_frame
FAILED"); break; } LOGI("avcodec_receive_frame %lld, nb_samples = %d",
frame->pts, frame->nb_samples); } }
调用MedieCodec硬解码关键代码
//添加头文件 #include <libavcodec/jni.h> //java加载的时候自动调用 extern "C" JNIEXPORT jint
JNI_OnLoad(JavaVM *vm, void *res) { //java虚拟机环境传递给ffmpeg av_jni_set_java_vm(vm,
0); return JNI_VERSION_1_4; } //硬解码 AVCodec *codec =
avcodec_find_decoder_by_name("h264_mediacodec");
热门工具 换一换