视频解码
初始化
- 视频常用的编解码器id定义(以h264和h265为例)
// 定义在ffmpeg\include\libavcodec\avcodec.h
AV_CODEC_ID_H264
AV_CODEC_ID_H265
- 查找解码器:根据编解码id查看解码器
AVCodec* pCodecVideo = avcodec_find_decoder(codecID);
if (!pCodecVideo)
{
printf("avcodec_find_decoder failed\n");
return -1;
}
- 申请编码器上下文结构体内存,保存了视频编解码相关信息
AVCodecContext* pCodecCtxVideo = avcodec_alloc_context3(pCodecVideo);
if (!pCodecCtxVideo)
{
printf("avcodec_alloc_context3 error\n");
return -1;
}
- 打开解码器
if (avcodec_open2(pCodecCtxVideo, pCodecVideo, NULL) < 0)
{
printf("avcodec_open2 failed\n");
return -1;
}
- 申请帧内存:存储一帧解码后像素(采样)数据
AVFrame* pFrameVideo = av_frame_alloc();
if (!pFrameVideo)
{
printf("av_frame_alloc failed\n");
return -1;
}
视频解码
- 解码一帧压缩数据
// data和len为压缩数据的指针和大小
AVPacket packet;
av_init_packet(&packet);
packet.data = (uint8_t*)data;
packet.size = len;
int got_picture = 0;
if (avcodec_decode_video2(pCodecCtxVideo, pFrameVideo, &got_picture, &packet) < 0)
{
printf("avcodec_decode_video2 failed\n");
return -1;
}
- 获取帧大小
// 以YUV420为例
int frameSize = avpicture_get_size(AV_PIX_FMT_YUV420P, pFrameVideo->linesize[0], pFrameVideo->height);
- 获取上下文,获取用于转码的参数**(初始化一次)**
// pFrameVideo->width:输入帧数据宽
// pFrameVideo->height:输入帧数据高
// pCodecCtxVideo->pix_fmt:帧数据格式
// pFrameVideo->width:输出帧数据宽
// pFrameVideo->height:输出帧数据高
// AV_PIX_FMT_YUV420P:输出帧数据格式,例如YUV420、RGB32等
// SWS_BICUBIC:视频像素数据格式转换算法类型
SwsContext* imgConvertCtx = sws_getContext(pFrameVideo->width,
pFrameVideo->height,
pCodecCtxVideo->pix_fmt,
pFrameVideo->width,
pFrameVideo->height,
AV_PIX_FMT_YUV420P,
SWS_BICUBIC, NULL, NULL, NULL);
- 缓冲区分配缓存**(初始化一次)**
int frameSize = avpicture_get_size(AV_PIX_FMT_YUV420P, pFrameVideo->width, pFrameVideo->height);
AVFrame* picture = av_frame_alloc();
uint8_t* pictureBuf = new uint8_t[frameSize];
- 初始化缓冲区**(初始化一次)**
avpicture_fill((AVPicture *)m_picture, m_pictureBuf, AV_PIX_FMT_YUV420P, pFrameVideo->width, pFrameVideo->height);
- 图片转换**(针对实时流或读取的文件流,循环调用)**
sws_scale(imgConvertCtx, (const uint8_t* const*)pFrameVideo->data, pFrameVideo->linesize, 0, pFrameVideo->height, picture->data, picture->linesize);
解码关闭
if (nullptr != pCodecCtxVideo)
{
avcodec_close(pCodecCtxVideo);
av_free(pCodecCtxVideo);
pCodecCtxVideo = nullptr;
}
if (nullptr != pFrameVideo)
{
av_frame_free(&pFrameVideo);
pFrameVideo = nullptr;
}
if (nullptr != picture)
{
av_frame_free(&picture);
picture = nullptr;
}
if (nullptr != pictureBuf)
{
delete[] pictureBuf;
pictureBuf = nullptr;
}
if (nullptr != imgConvertCtx)
{
sws_freeContext(imgConvertCtx);
imgConvertCtx = nullptr;
}
音频解码
初始化
- 音频常用的编解码器id定义
AV_CODEC_ID_PCM_ALAW
AV_CODEC_ID_PCM_MULAW
AV_CODEC_ID_FIRST_AUDIO
AV_CODEC_ID_AAC
- 查找解码器:根据编解码id查看解码器
AVCodec* pCodecAudio = avcodec_find_decoder(codecID);
if (!pCodecAudio)
{
printf("audio avcodec_find_decoder failed\n");
return -1;
}
- 申请编码器上下文结构体内存,保存了音频编解码相关信息
AVCodecContext* pCodecCtxAudio = avcodec_alloc_context3(pCodecAudio);
if (!pCodecCtxAudio)
{
printf("audio avcodec_alloc_context3 failed\n");
return -1;
}
- 打开解码器
int audioCodecType = (int)codec;
switch (audioCodecType)
{
case CODEC_AUDIO_AAC:
break;
case CODEC_AUDIO_MP3:
break;
case CODEC_AUDIO_G711:
case CODEC_AUDIO_G711U:
pCodecCtxAudio->codec_type = AVMEDIA_TYPE_AUDIO;
pCodecCtxAudio->sample_fmt = AV_SAMPLE_FMT_S16;
pCodecCtxAudio->sample_rate = 8000;
pCodecCtxAudio->channel_layout = AV_CH_LAYOUT_MONO;
pCodecCtxAudio->channels = 1;
break;
case CODEC_AUDIO_G7231:
break;
case CODEC_AUDIO_G7221:
break;
default:
break;
}
pCodecCtxAudio->codec_id = codecID;
int ret = avcodec_open2(pCodecCtxAudio, pCodecAudio, NULL);
if (ret < 0)
{
printf("audio avcodec_open2 failed\n");
return -1;
}
- 申请内存和初始化参数
AVFrame* frameAudio = av_frame_alloc();
if (!frameAudio)
{
printf("audio av_frame_alloc failed\n");
return -1;
}
AVPacket* audioPacket = av_packet_alloc();
if (!audioPacket)
{
printf("av_packet_alloc failed\n");
return -1;
}
av_init_packet(audioPacket);
音频解码
- 解码一帧音频数据
audioPacket->data = (uint8_t*)data;
audioPacket->size = datalen;
int ret = avcodec_send_packet(m_pCodecCtxAudio, m_audioPacket);
if (ret < 0)
{
av_packet_unref(audioPacket);
printf("audio avcodec_send_packet failed\n");
return -1;
}
- 接收一帧数据
ret = avcodec_receive_frame(m_pCodecCtxAudio, m_frameAudio);
if (ret < 0)
{
return -1;
}
- 设置输入和输出音频信息**(执行一次)**
// 分配SwrContext
SwrContext* audioSwrCtx = swr_alloc();
int channelLayout = av_get_default_channel_layout(frameAudio->channels);
// audioSwrCtx:重采样申请的内存。如果传NULL,内部会申请一块内存,非NULL可以复用之前的内存
// AV_CH_LAYOUT_MONO:目标声道
// AV_SAMPLE_FMT_S16:目标采样格式
// frameAudio->sample_rate:目标采样率
// channelLayout:原始声道布局
// pCodecCtxAudio->sample_fmt:原始采样格式
// frameAudio->sample_rate:原始采样率
// 设置输入和输出的音频信息
swr_alloc_set_opts(audioSwrCtx,
AV_CH_LAYOUT_MONO,
AV_SAMPLE_FMT_S16,
frameAudio->sample_rate,
channelLayout,
pCodecCtxAudio->sample_fmt,
frameAudio->sample_rate, 0, NULL);
// 设置用户参数后初始化上下文
swr_init(audioSwrCtx);
- 重采样转换(循环执行)
// audioSwrCtx:音频重采样的上下文
// audioBuffer:输出的指针。传递的输出的数组
// 1024*256:输出的样本数量,不是字节数。单通道的样本数量。
// (const uint8_t**)frameAudio->data:输入的数组,AVFrame解码出来的DATA
// frameAudio->nb_samples:输入的单通道的样本数量。
// 以单声道为例
int len = swr_convert(audioSwrCtx,
&audioBuffer,
1024*256,
(const uint8_t**)frameAudio->data,
frameAudio->nb_samples);
// 获取音频大小
av_get_channel_layout_nb_channels(AV_CH_LAYOUT_MONO);
int bufSize = av_samples_get_buffer_size(NULL,
av_get_channel_layout_nb_channels(AV_CH_LAYOUT_MONO),
frameAudio->nb_samples,
AV_SAMPLE_FMT_S16,
0);
解码关闭
if (nullptr != pCodecCtxAudio)
{
avcodec_close(pCodecCtxAudio);
av_free(pCodecCtxAudio);
pCodecCtxAudio = nullptr;
}
if (nullptr != frameAudio)
{
av_frame_free(&frameAudio);
frameAudio = nullptr;
}
if (nullptr != audioPacket)
{
av_packet_unref(audioPacket);
av_packet_free(&audioPacket);
audioPacket = nullptr;
}
if (nullptr != audioSwrCtx)
{
swr_free(&audioSwrCtx);
audioSwrCtx = nullptr;
}
// 其他资源释放