笔者在嵌入式领域深耕6年,对嵌入式项目构建,BLDC电机控制,产品上位机开发以及产品量产和产品售后维护有多年工作经验。经验分享,从0到1, 让我带你从实际工作的角度走进嵌入式成长之路。
原创不易,欢迎大家关注我的微信公众号:嵌入式工程师成长之路 或 扫下面二维码
所有文章总目录:【电子工程师 qt工程师】
原创视频总目录:【电子工程师 qt工程师】
大概步骤:通过解码器ID找到对应的解码器,然后再创建一个解码器上下文,再通过解码器上下文打开解码器。
(1)void avcodec_register_all(void)
:注册所有的解码器。
(2)AVCodec *avcodec_find_decoder(enum AVCodecID id)
:通过解码器ID找到对应的解码器。
(3)AVCodec *avcodec_find_decoder_by_name(const char *name)
:通过名字找到对应的解码器,硬解码方式时,一般会使用这种方式。
AVCodec结构体仅仅是一些解码相关的配置信息,真正解码时,还需要一个解码器上下文AVCodecContext,解码器上下文存放的是本次解码的参数信息。
(4)AVCodecContext *avcodec_alloc_context3(const AVCodec *codec)
:根据解码创建解码器上下文。老版本的ffmpeg不需要这一步,这一步耦合在解封装的函数内部。但新版本的将解封装和解码进行了分离,所以需要先分配一个上下文空间。
(5)void avcodec_free_context(AVCodecContext **avctx)
:清理解码器上下文空间。
(6)int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
:打开解码器。第二个参数可以直接传NULL,因为在avcodec_alloc_context3函数中已经指定了。第三个参数指解码过程的参数(所有的参数在:/libavcodec/options_table.h中),一般直接使用默认即可。
(7)int avcodec_parameters_to_context(AVCodecContext *codec, const AVCodecParameters *par)
:将AVStreame中的codecpar参数复制到解码器上下文结构体来。
硬解码出来的视频格式和软解码出来的视频格式是不一样的,或者解码出来的是YUV444格式的,需要转换为YUV420的,或者解码出来的是BGR的,需要转换为RGB的,这里都涉及到像素格式的转换。
直接使用ffmpeg接口来转换,用ffmpeg的好处就是接口简单,但性能开销还是比较大。这部分可以用gpu来做,效率会更高;实际产品中,建议还是用GPU来做。
① struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,int dstW, int dstH, enum AVPixelFormat dstFormat,int flags, SwsFilter *srcFilter,SwsFilter *dstFilter, const double *param)
:获得一个像素格式转换的上下文。
struct SwsContext *sws_getContext(
int srcW,//原宽
int srcH,//原高
enum AVPixelFormat srcFormat,//原来的格式
int dstW,//目标宽
int dstH,//目标高
enum AVPixelFormat dstFormat,//目标格式
int flags,//只针对尺寸转换的算法,一般选择SWS_FAST_BILINEAR就可以了
SwsFilter *srcFilter,//过滤器一般用不到,直接传NULL
SwsFilter *dstFilter,//过滤器一般用不到,直接传NULL
const double *param);//这个是和转换算法有关的,譬如使用矩阵算法进行转换,可以通过
//param传递一个矩阵,我们直接作用默认就可以了,即直接传NULL
#define SWS_FAST_BILINEAR 1 //一般直接使用这种方式就可以了
#define SWS_BILINEAR 2
#define SWS_BICUBIC 4
#define SWS_X 8
#define SWS_POINT 0x10
#define SWS_AREA 0x20
#define SWS_BICUBLIN 0x40
#define SWS_GAUSS 0x80
#define SWS_SINC 0x100
#define SWS_LANCZOS 0x200
#define SWS_SPLINE 0x400
② int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],const int srcStride[], int srcSliceY, int srcSliceH,uint8_t *const dst[], const int dstStride[])
:sws_getContext这个函数仅仅是获得一个转换上下文,而真正的转换则需要使用sws_scale函数。
③ void sws_freeContext(struct SwsContext *swsContext)
:释放转换上下文空间,这里传递的不是指针的指针,也就是sws_freeContext函数内部没有将swsContext置为NULL,所以需要手动置为NULL。
只有解码之后的音频数据才需要重采样,因为直接解码出来的音频数据是32bit的,即不能直接播放的,需要进行转换,一般是转为16bit的。
① struct SwrContext *swr_alloc(void)
:设置重采样上下文。
② struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,int log_offset, void *log_ctx)
:设置重采样上下文参数。
struct SwrContext *swr_alloc_set_opts(
struct SwrContext *s,
//输出格式设置
int64_t out_ch_layout,//声道类型,是立体声,还是其他
enum AVSampleFormat out_sample_fmt, //输出的样本格式,有float的也有s16的
int out_sample_rate,//样本率可以不变,输出和输入一样。当然可以改变采样率是播放速度变快
//但这种变速会使声音失真
//输入格式设置
int64_t in_ch_layout,
enum AVSampleFormat in_sample_fmt,
int in_sample_rate,
int log_offset,
void *log_ctx
);
③ int swr_init(struct SwrContext *s)
:设置好参数之后,就进行初始化。
④ void swr_free(struct SwrContext **s)
:释放空间。
⑤ int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,const uint8_t **in , int in_count)
:将一帧一帧的音频数据进行重采样。
int swr_convert(struct SwrContext *s,
uint8_t **out,
int out_count,//单通道的样本数量
const uint8_t **in, //输入的数据,直接使用avframe->data即可
int in_count);//单通道的样本数量