本教程采用的ffmpeg是3.2版本,SDL是2.0版本,Qt控制台程序,编译器是mingwin64,在window下创建的工程,没有测试linux是否可用,用户可自行测试。另外本教程是在雷神的基础上“翻新”的,雷神采用的是旧版本的ffmpeg,由于新版本的ffmpeg修改了很多流程和API,而且雷神的代码是解析的yuv文件,我们经常遇到MP4文件,需要一个能直接播放MP4的例程。所以本人根据新版本的API函数修改了代码,供大家交流使用。(在此感谢雷神!)
1、在配置Qt、ffmpeg、sdl过程中编译的时候出现未定义的winmain函数,这个问题是由于SDL的头文件引起的,需要在main.cpp文件的前面加上#undef main,如下图所示。
1、ffmpeg解码流程图
2、SDL流程图(与原来一致)
其中SDL_Window个人理解就是一个画板,可以在上面创建多个Rect区域,这些区域都是用来显示视频画面的;SDL_Texture是一个纹理特征对象,即是一帧画面,用于渲染在Rect区域;而SDL_Renderer就是一个渲染器,负责渲染画面。(参考雷神的关系图,一目了然)
做监控视频时候的多窗口显示,可以这样设计,利用Rect
#include
#ifdef __cplusplus
#define __STDC_CONSTANT_MACROS
#ifdef _STDINT_H
#undef _STDINT_H
#endif
# include
#endif
extern "C"
{
#include"libavcodec/avcodec.h"
#include"libavformat/avformat.h"
#include"libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include"SDL.h"
}
#undef main
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
AVFormatContext *pFormatCtx;
int i,videoindex;
AVCodecContext *pCodecCtx;
AVCodec * pCodec;
AVFrame * pFrame,*pFrameYUV;
uint8_t *out_buffer;
AVPacket * packet;
struct SwsContext *img_convert_ctx;
int screen_w,screen_h;
SDL_Window *screen;
SDL_Renderer*sdlRenderer;
SDL_Texture *sdlTexture;
SDL_Rect sdlRect;
FILE* fp_yuv;
int ret,got_picture;
char filepath[]="d://test.MP4";
av_register_all();
avformat_network_init();
pFormatCtx=avformat_alloc_context();
if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0)
{
printf("无法打开信息流");
return -1;
}
if(avformat_find_stream_info(pFormatCtx,NULL)<0)
{
printf("无法查找到流信息");
return -1;
}
videoindex=-1;
for(i=0;inb_streams;i++)
{
if(pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoindex=i;
break;
}
}
if(videoindex<0)
{
printf("没有视频流");
return -1;
}
pCodecCtx=avcodec_alloc_context3(NULL);
if(!pCodecCtx)
{
printf("分配解码器上下文内存失败");
return -1;
}
//获取编解码器
if(avcodec_parameters_to_context(pCodecCtx,pFormatCtx->streams[videoindex]->codecpar)<0)
{
printf("拷贝视频流解码器参数到解码器对象失败");
return -1;
}
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(!pCodec)
{
printf("查找解码器失败");
return -1;
}
//打开解码器
if(avcodec_open2(pCodecCtx,pCodec,NULL)<0)
{
printf("打开解码器失败");
return -1;
}
printf("------------------------------视频信息-----------------------\n");
av_dump_format(pFormatCtx,0,filepath,0);
printf("------------------------------视频信息-----------------------\n");
pFrame=av_frame_alloc();
pFrameYUV=av_frame_alloc();
out_buffer=(uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1));
av_image_fill_arrays(pFrameYUV->data,pFrameYUV->linesize,out_buffer,AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1);
packet=(AVPacket*)av_malloc(sizeof (AVPacket));
img_convert_ctx=sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,
pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
if(SDL_Init(SDL_INIT_VIDEO| SDL_INIT_AUDIO|SDL_INIT_TIMER))
{
printf("无法初始化SDL");
return -1;
}
screen_w=pCodecCtx->width;
screen_h=pCodecCtx->height;
screen=SDL_CreateWindow("播放器",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,screen_w,screen_h,SDL_WINDOW_OPENGL);
if(!screen)
{
printf("创建播放窗口失败");
return -1;
}
sdlRenderer=SDL_CreateRenderer(screen,-1,0);
sdlTexture=SDL_CreateTexture(sdlRenderer,SDL_PIXELFORMAT_IYUV,SDL_TEXTUREACCESS_STREAMING,screen_w,screen_h);
sdlRect.x=0;
sdlRect.y=0;
sdlRect.w=screen_w;
sdlRect.h=screen_h;
while (av_read_frame(pFormatCtx,packet)>=0) {
if(packet->stream_index==videoindex)
{
ret=avcodec_send_packet(pCodecCtx,packet);
if(ret<0)
{
printf("发送失败");
}
while (ret>=0) {
ret=avcodec_receive_frame(pCodecCtx,pFrame);
if(ret==AVERROR(EAGAIN)||ret==AVERROR_EOF)
{
break;
}
else if(ret<0)
{
//释放资源
// av_frame_unref(pFrame);
// av_frame_unref(pFrameYUV);
}
if(ret>=0)
{
sws_scale(img_convert_ctx,pFrame->data,pFrame->linesize,0,pCodecCtx->height,pFrameYUV->data,pFrameYUV->linesize);
#if 0
SDL_UpdateTexture(sdlTexture,NULL,pFrameYUV->data[0],pFrameYUV->linesize[0]);
#else
SDL_UpdateYUVTexture(sdlTexture,&sdlRect,
pFrameYUV->data[0],pFrameYUV->linesize[0],
pFrameYUV->data[1],pFrameYUV->linesize[1],
pFrameYUV->data[2],pFrameYUV->linesize[2]);
#endif
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer,sdlTexture,NULL,&sdlRect);
SDL_RenderPresent(sdlRenderer);
SDL_Delay(40);
}
}
}
av_packet_unref(packet);
}
SDL_Quit();
av_frame_unref(pFrame);
av_frame_unref(pFrameYUV);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return a.exec();
}