最简单的FFmpeg+SDL+Qt视频播放器-播放MP4文件

简介

本教程采用的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 <QCoreApplication>
#ifdef __cplusplus
 #define __STDC_CONSTANT_MACROS
 #ifdef _STDINT_H
  #undef _STDINT_H
 #endif
 # include <stdint.h>
#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;i<pFormatCtx->nb_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();
}

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值