音视频学习(十八)——使用ffmepg实现视音频解码

视频解码

初始化

  1. 视频常用的编解码器id定义(以h264和h265为例)
// 定义在ffmpeg\include\libavcodec\avcodec.h
AV_CODEC_ID_H264
AV_CODEC_ID_H265
  1. 查找解码器:根据编解码id查看解码器
AVCodec* pCodecVideo = avcodec_find_decoder(codecID);
if (!pCodecVideo)
{
    printf("avcodec_find_decoder failed\n");
    return -1;
}
  1. 申请编码器上下文结构体内存,保存了视频编解码相关信息
AVCodecContext* pCodecCtxVideo = avcodec_alloc_context3(pCodecVideo);
if (!pCodecCtxVideo)
{
    printf("avcodec_alloc_context3 error\n");
    return -1;
}
  1. 打开解码器
if (avcodec_open2(pCodecCtxVideo, pCodecVideo, NULL) < 0)
{
    printf("avcodec_open2 failed\n");
    return -1;
}
  1. 申请帧内存:存储一帧解码后像素(采样)数据
AVFrame* pFrameVideo = av_frame_alloc(); 
if (!pFrameVideo)
{
    printf("av_frame_alloc failed\n");
    return -1;
}

视频解码

  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;
}
  1. 获取帧大小
// 以YUV420为例
int frameSize = avpicture_get_size(AV_PIX_FMT_YUV420P, pFrameVideo->linesize[0], pFrameVideo->height);
  1. 获取上下文,获取用于转码的参数**(初始化一次)**
// 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);
  1. 缓冲区分配缓存**(初始化一次)**
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];
  1. 初始化缓冲区**(初始化一次)**
avpicture_fill((AVPicture *)m_picture, m_pictureBuf, AV_PIX_FMT_YUV420P, pFrameVideo->width, pFrameVideo->height);
  1. 图片转换**(针对实时流或读取的文件流,循环调用)**
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;
}

音频解码

初始化

  1. 音频常用的编解码器id定义
AV_CODEC_ID_PCM_ALAW
AV_CODEC_ID_PCM_MULAW
AV_CODEC_ID_FIRST_AUDIO
AV_CODEC_ID_AAC
  1. 查找解码器:根据编解码id查看解码器
AVCodec* pCodecAudio = avcodec_find_decoder(codecID);
if (!pCodecAudio)
{
    printf("audio avcodec_find_decoder failed\n");
    return -1;
}
  1. 申请编码器上下文结构体内存,保存了音频编解码相关信息
AVCodecContext* pCodecCtxAudio = avcodec_alloc_context3(pCodecAudio);
if (!pCodecCtxAudio)
{
    printf("audio avcodec_alloc_context3 failed\n");
    return -1;
}
  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;
}
  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);

音频解码

  1. 解码一帧音频数据
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;
}
  1. 接收一帧数据
ret = avcodec_receive_frame(m_pCodecCtxAudio, m_frameAudio);
if (ret < 0)
{
    return -1;
}
  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);
  1. 重采样转换(循环执行)
// 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;
}

// 其他资源释放
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值