5.1 avcodec_find_decoder、AVCodecContext、avcodec_parameters_to_context
1、确定解码器,通过avcodec_find_decoder获取解码器,返回AVCodec这个结构体
avcodec_register_all(); // 注册 所有解码器
AVCodec *avcodec_find_decoder(enum AVCodecID id) // 通过传递id号来获取对应的解码器
AVCodec *avcodec_find_decoder_by_name(const char * name) // 通过字符串获取解码器————硬解码
avcodec_find_decoder_by_name("h264_mediacodec"); // 通过名字
2、解码时候还需要解码上下文 AVCodecContext(本次解码的信息)
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec) // 申请创建上下文空间
void avcodec_free_context(AVCodecContext **avctx) // 空间释放
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec,AVDictionary **options); // 打开解码器
/libavcodec/options_table.h 参数设置
int thread_count; // 多线程解码,充分利用CPU资源
time_base; // 时间基数
3、参数设置,除了手动设置,还可以通过函数avcodec_parameters_to_context
avcodec_parameters_to_context,将参数设置直接进行拷贝,从AVStream里面进行拷贝
5.3 AVFrame (在打开解码器上下文之后开始逐帧进行解码,解码前需要先关注AVFrame这个结构体,用于存放解码后的数据)
AVFrame就是一幅独立的图像!!!
AVFrame * frame = av_frame_alloc() // 分配空间(分配和释放与AVPacket相同)
void av_frame_free(AVFrame **frame) // 空间释放
int av_frame_ref(AVFrame *dst, const AVFrame *src); // 引用计数
AVFrame *av_frame_clone(const AVFrame *src); // 复制,重新创建空间,引用空间+1
void av_frame_unref(AVFrame *frame); // 直接引用计数减一(空间释放方法二)
uint8_t *data[AV_NUM_DATA_POINTERS];
int linesize[AV_NUM_DATA_POINTERS];
int width,height; int nb_samples; // nb_samples单通道的样本数量,一个样本2字节
int64_t pts; int64_t pkt_dts;
int sample_rate; uint64_t channel_layout; int channels;
int format; // AVPixelFormat AVSapleFormat; // 像素格式
linesize:存放大小,目的是为了对齐
宽度一行数据的大小
通道一行数据的大小
5.4 解码函数
avcodec_send_packet,将packet写到解码队列中去
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); // 内存开销问题
avcodec_receive_frame,从已解码成功的数据中取出一个frame
int avcodec_receive_frame(AVCodecContext *avctx,AVFrame *frame);
ps:send一次,receive不一定是一次
问题集锦:1、播放最后几帧显示不了,那是缓冲帧的问题。传null,(解决播放时候最后几帧不显示的问题)
// 发送packet 到解码线程,send传NULL(将pkt换成null)后调用多次receive,会取出所有的缓冲帧
re = avcodec_send_packet(cc,pkt);
2、要加头文件
包含了库之后要把对应的头文件也包含进来
3 重采样有报错
5.6 视频像素格式转换和尺寸转换(可以用GPU来做,效率更高)ffmpeg接口简单(唯一优势),但性能开销大
sws_getContext 像素格式转换上下文,每次都会新创建
struct SwsContext *sws_getCachedContext(struct SwsContext *context(传格式转换上下文地址),
int srcW(原宽), int srcH(原高), enum AVPixelForamt srcFormat(原像素格式),
int dstW(目标宽), int dstH(目标高), enum AVPixelFormat dstFormat(目标像素格式),
int flags, SwsFilter * srcFilter,
SwsFilter *dstFilter, const double *param );
第一个函数sws_getCachedContext只是创建好像素格式转换的上下文,具体的逐帧转换用sws_scale
int sws_scale(struct Swscontext * c, const uint8_t *const srcSlice[],
const int srcStride[](linesize,一行大小,宽度), int srcSliceY(0), int srcSliceH(图像高度);
uint8_t *const dst[](目标地址), const int dstStride[](输出的一行大小,linesize));
void sws_freeContext(struct SwsContext *swsContext); // 释放上下文,传地址就可以
int flags, SwsFilter * srcFilter, 这里的flag指的是各种算法,主要是针对尺寸的变化,不涉及像素格式转化,具体如下
5.9 音频重采样
SwrContext
音频解码出来不能直接播放,需要重采样(解码出来是32位,声卡不支持,所以需要重采样)
ffmpeg所有处理都需要上下文,因为是C 语言的一个特点,上下文,不像C++ 有个对象就行了,而C语言需要有个指针贯穿前后,将他们关联起来,所以有这么一个结构体的上下文
SwrContext *swr_alloc(void); // 分配和初始化
SwrContext *swr_alloc_set_opts(
struct SwrContext *s, //重采样上下文
int64_t out_ch_layout, // 输出的声道标准
AVSampleFormat out_sample_fmt, // 输出的样本格式
int out_sample_rate, // 输出的样本率
int64_t in_ch_layout,
AVSampleFormat in_sample_fmt, // 输入的格式
int in_sample_rate, // 输入的样本率
int log_offset,void *log_ctx); // 两个日志传0就行
int swr_init(struct SwrContext *s); // 初始化上下文,然后进行格式转换
void swr_free