FFmpeg开发(三)——(FFmpeg的简单应用)
前两篇介绍了FFmpeg的安装和基本知识。
FFmpeg开发(一)一Qt Creator配置FFmpeg
今天整理下ffmpeg的简单应用。
在上篇中,可以知道FFmpeg的解码流程。
通过上图,可以看到解码流程。(图片来源于网络)
按照流程,我们整理出一个完整的流程代码:
int main()
{
QString fileName = "C:/Users/feng/Desktop/test.mp4";
//ffmpeg相关变量预先定义与分配
AVFormatContext *m_pAVFormatCtx; //解封装上下文
SwsContext *m_pSwsCtx; //视频转码上下文
AVCodecContext *avCodecCtx; //解码上下文
AVFrame *m_pAVFrame = 0; // ffmpeg单帧缓存
AVFrame *m_pAVFrameRGB32 = 0; // ffmpeg单帧缓存转换颜色空间后的缓存
AVPacket *m_pAVPacket = 0; // ffmpag单帧数据包
int gotPicture = 0; // 解码时数据是否解码成功
int numBytes = 0; // 解码后的数据长度
uchar *outBuffer = 0; // 解码后的数据存放缓存区
char m_errorBuff[1024]; //打开时发生的错误信息
int m_totalMs; //总时长
int m_videoStream; //视频流
int m_audioStream; //音频流
int m_fps; //每秒的视频帧数
int m_pts; //获得当前解码帧的时间
int m_sampleRate; //样本率
int m_sampleSize; //样本大小
int m_channel; //通道数
m_pAVFormatCtx= avformat_alloc_context();
m_pAVPacket = av_packet_alloc();
m_pAVFrame = av_frame_alloc();
m_pAVFrameRGB32 = av_frame_alloc();
// 步骤一:注册所有容器和编解码器
av_register_all();
// 步骤二:打开文件(ffmpeg成功则返回0)
int ret = avformat_open_input(&m_pAVFormatCtx, fileName , 0, 0);//打开解封装器
if (ret != 0)//打开错误时
{
av_strerror(ret, m_errorBuff, sizeof(m_errorBuff));//错误信息
printf("open %s failed :%s\n", path, m_errorBuff);
return 0;
}
m_totalMs = m_pAVFormatCtx->duration / (AV_TIME_BASE);//获取视频的总时间
// 步骤三:提取流信息,提取视频信息
for(int index = 0; index < m_pAVFormatContext->nb_streams; index++)
{
avCodecCtx= m_pAVFormatContext->streams[index]->codec;
switch (avCodecCtx->codec_type)
{
case AVMEDIA_TYPE_VIDEO: //视频流
m_videoStream = i;
break;
case AVMEDIA_TYPE_AUDIO: //音频流
m_audioStream = i;//音频流
break;
default:
break;
}
// 已经找打视频品流
if(m_videoStream != -1)
{
break;
}
}
if(m_videoStream == -1 || !avCodecCtx)
{
return -1;
}
// 步骤四:对找到的视频流寻解码器
pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id);
if(!pAVCodec)
{
LOG << "Fialed to avcodec_find_decoder(pAVCodecContext->codec_id):"
<< pAVCodecContext->codec_id;
goto END;
}
AVCodec *avCodec = avcodec_find_decoder(avCodecCtx->codec_id);//查找解码器
if (!avCodec)//未找到解码器
{
printf("video code not find\n");
return -1;
}
// 步骤五:打开解码器
int err = avcodec_open2(avCodecCtx, avCodec, NULL);//打开解码器
if (err != 0)//未打开解码器
{
char buf[1024] = { 0 };
av_strerror(err, buf, sizeof(buf));
printf("not open! %s",buf);
return 0;
}
printf("open codec success!\n");
// 步骤六:对拿到的原始数据格式进行缩放转换为指定的格式高宽大小
pSwsContext = sws_getContext(avCodecCtx->width,
avCodecCtx->height,
avCodecCtx->pix_fmt,
avCodecCtx->width,
avCodecCtx->height,
AV_PIX_FMT_RGBA,
SWS_FAST_BILINEAR,
0,
0,
0);
numBytes = avpicture_get_size(AV_PIX_FMT_RGBA,
avCodecCtx->width,
avCodecCtx->height);
outBuffer = (uchar *)av_malloc(numBytes);
avpicture_fill((AVPicture *)m_pAVFrameRGB32 ,
outBuffer,
AV_PIX_FMT_RGBA,
avCodecCtx->width,
avCodecCtx->height);
// 步骤七:读取一帧数据的数据包
while(av_read_frame(m_pAVFormatContext, m_pAVPacket ) >= 0)
{
if(pAVPacket->stream_index == m_videoStream )
{
// 步骤八:对读取的数据包进行解码
ret = avcodec_decode_video2(avCodecCtx, m_pAVFrame , &gotPicture, m_pAVPacket );
if(ret < 0)
{
break;
}
// 等于0代表拿到了解码的帧数据
if(!gotPicture)
{
break;
}else{
ws_scale(m_pSwsCtx,
(const uint8_t * const *)m_pAVFrame ->data,
m_pAVFrame ->linesize,
0,
avCodecCtx->height,
m_pAVFrameRGB32 ->data,
m_pAVFrameRGB32 ->linesize);
QImage imageTemp((uchar *)outBuffer,
avCodecCtx->width,
avCodecCtx->height,
QImage::Format_RGBA8888);
QImage image = imageTemp.copy();
}
av_free_packet(pAVPacket);
}
QThread::msleep(100);
}
}
下一篇,根据这些流程写一个视频播放器。
本文原创作者:冯一川(ifeng12358@163.com),未经作者授权同意,请勿转载。