zoukankan      html  css  js  c++  java
  • QT+FFMPEG+SDL2.0实现视频播放

    开发环境:MinGW+QT5.9+FFMPEG20190212+SDL2.0.9

    一、开发环境搭建

    (1)下载工具

    https://ffmpeg.zeranoe.com/builds/下载对应版本。链接方式有三种,

    Static:这个版本只包含了ffmpeg.exeffplay.exeffprobe.exe三个可执行程序,没有头文件和库文件。

    Shared:这个版本包含了ffmpeg.exeffplay.exeffprobe.exe三个可执行程序和相关动态库文件。

    Dev:开发版,这个包含了头文件和库文件。

    我们需要下载SharedDev两个版本,Dev有我们程序开发需要的头文件和库文件,这里面包含的库是动态调用的,所依赖的动态库在Shared这个版本里面,所以两个版本都要下载。

    http://www.libsdl.org/download-2.0.php下载SDL库,选择

    (2)添加库

    将下载的文件解压缩,然后新建一个QT工程,在pro添加lib目录和include目录的路径。

    INCLUDEPATH +="E:\Lib\ffmpeg\include"
    INCLUDEPATH +="E:\Lib\SDL2-2.0.9\include"
    
    LIBS += -LE:Libffmpeglib -lavutil -lavformat -lavcodec -lavdevice -lavfilter -lpostproc -lswresample -lswscale
    LIBS += -LE:LibSDL2-2.0.9libx86 -lSDL2

    然后将ffmpeg的dll和SDL2.dll放到exe目录下。

    二、代码实现:

    在QT界面上放置一个widget和一个按钮,点击按钮时实现下面功能:

    extern "C"{
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
    #include <libavutil/imgutils.h>
    #include <SDL.h>
    }
    
    void MainWindow::on_btnPlay_clicked()
    {
        AVFormatContext    *pFormatCtx;
        int                i, videoindex;
        AVCodecContext    *pCodecCtx;
        AVCodec            *pCodec;
        AVFrame    *pFrame, *pFrameYUV;
        unsigned char *out_buffer;
        AVPacket *packet;
        int ret, got_picture;
        struct SwsContext *img_convert_ctx;
    
        char filepath[] = "E:\media\1.avi";
        //初始化编解码库
        av_register_all();
        //已经无需使用的函数?
        //avformat_network_init();
        //创建AVFormatContext对象,与码流相关的结构。
        pFormatCtx = avformat_alloc_context();
        //初始化pFormatCtx结构
        if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0){
            printf("Couldn't open input stream.
    ");
            return ;
        }
        //获取音视频流数据信息
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0){
            printf("Couldn't find stream information.
    ");
            return ;
        }
        videoindex = -1;
        //nb_streams视音频流的个数,这里当查找到视频流时就中断了。
        for (i = 0; i < pFormatCtx->nb_streams; i++)
            if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
                videoindex = i;
                break;
        }
        if (videoindex == -1){
            printf("Didn't find a video stream.
    ");
            return ;
        }
        //获取视频流编码结构
        pCodecCtx = pFormatCtx->streams[videoindex]->codec;
        //查找解码器
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
        if (pCodec == NULL){
            printf("Codec not found.
    ");
            return ;
        }
        //用于初始化pCodecCtx结构
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
            printf("Could not open codec.
    ");
            return ;
        }
        //创建帧结构,此函数仅分配基本结构空间,图像数据空间需通过av_malloc分配
        pFrame = av_frame_alloc();
        pFrameYUV = av_frame_alloc();
        //创建动态内存,创建存储图像数据的空间
        //av_image_get_buffer_size获取一帧图像需要的大小
        out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, ui->widget->width(), ui->widget->height(), 1));
        av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,
            AV_PIX_FMT_YUV420P, ui->widget->width(), ui->widget->height(), 1);
    
        packet = (AVPacket *)av_malloc(sizeof(AVPacket));
        //Output Info-----------------------------
        printf("--------------- File Information ----------------
    ");
        //此函数打印输入或输出的详细信息
        av_dump_format(pFormatCtx, 0, filepath, 0);
        printf("-------------------------------------------------
    ");
        //初始化img_convert_ctx结构
        img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
            ui->widget->width(), ui->widget->height(), AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
        //SDL---------------------------
        SDL_Window *screen;
        SDL_Renderer* sdlRenderer;
        SDL_Texture* sdlTexture;
        SDL_Rect sdlRect;
        if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
            printf("Could not initialize SDL - %s
    ", SDL_GetError());
            return;
        }
        screen = SDL_CreateWindowFrom((void *)ui->widget->winId());
        if(screen==NULL)
        {
            printf("Could not create window - %s
    ", SDL_GetError());
            return;
        }
        sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
        sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
        sdlRect.x = 0;
        sdlRect.y = 0;
        sdlRect.w = pCodecCtx->width;
        sdlRect.h = pCodecCtx->height;
        //end SDL-----------------------
        //av_read_frame读取一帧未解码的数据
        while (av_read_frame(pFormatCtx, packet) >= 0){
            //如果是视频数据
            if (packet->stream_index == videoindex){
                //解码一帧视频数据
                ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
                if (ret < 0){
                    printf("Decode Error.
    ");
                    return ;
                }
                if (got_picture){
                    sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                        pFrameYUV->data, pFrameYUV->linesize);
                    SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
                        pFrameYUV->data[0], pFrameYUV->linesize[0],
                        pFrameYUV->data[1], pFrameYUV->linesize[1],
                        pFrameYUV->data[2], pFrameYUV->linesize[2]);
                    SDL_RenderClear(sdlRenderer);
                    SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
                    SDL_RenderPresent(sdlRenderer);
                    SDL_Delay(40);
                }
            }
            av_free_packet(packet);
        }
        sws_freeContext(img_convert_ctx);
        av_frame_free(&pFrameYUV);
        av_frame_free(&pFrame);
        avcodec_close(pCodecCtx);
        avformat_close_input(&pFormatCtx);
    }
  • 相关阅读:
    更新自己知识的网站
    CRM IFD 部署在同一台服务器上遇到的错误
    Dynamics CRM 修改Excel 最大导出记录限制及 最大上传文件限制
    CRM 插件奇怪的报错
    单实例,当MongoDB单表数据文件太大导致写入速度变慢
    MongoDB分片实战
    SpringCloud使用feign时的复杂参数传递(转)
    99%的人都理解错了HTTP中GET与POST的区别(转)
    MySQL 数据导入 Unknown MySQL server host 'localhost'
    会员积分体系设计思路(转)
  • 原文地址:https://www.cnblogs.com/WushiShengFei/p/10837959.html
Copyright © 2011-2022 走看看