Qt +FFmpeg+SDL視頻播放器

調用ffmpeg庫對本地視頻文件進行視頻流解碼,解碼後的數據格式爲yuv420p,調用sdl庫,並傳入Qt顯示控件句柄給sdl進行渲染顯示,代碼如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLabel>
#include <QDebug>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;

    QLabel *label;

    QRect rect;

    bool isplay;

private slots:
    void start_play();

    void pause_play();

    void stop_play();


};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

extern "C"
{
 #include "SDL.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
}

#define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)

#define SFM_BREAK_EVENT  (SDL_USEREVENT + 2)

int thread_exit=0;
int thread_pause=0;

int wait_thread(void *)
{
    thread_exit=0;
    thread_pause=0;

    while (thread_exit==0)
    {
        if(!thread_pause)
        {
            SDL_Event event;
            event.type = SFM_REFRESH_EVENT;
            SDL_PushEvent(&event);
        }

        SDL_Delay(40);

    }

    SDL_Event event;
    event.type = SFM_BREAK_EVENT;
    SDL_PushEvent(&event);
    thread_exit=0;
    thread_pause=0;
    return 0;
}

#pragma execution_character_set("utf-8")  //加入這行
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setFixedSize(661,465);
    label = new QLabel(this);
    label->resize(601,381);
    label->move(30,20);
    rect = label->geometry();//記錄widget位置,恢復時使用
    isplay = false;
    connect(ui->start_Button,&QPushButton::clicked,this,&Widget::start_play);
    connect(ui->pause_Button,&QPushButton::clicked,this,&Widget::pause_play);
    connect(ui->stop_Button,&QPushButton::clicked,this,&Widget::stop_play);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::start_play()
{
    //在播放過程中判斷是否重複點擊播放按鈕
    if(!isplay)
    {
        isplay = true;
    }
    else
    {
        return;
    }

    AVFormatContext *inputctx;
    int i,videoindex;
    AVCodecContext *pCodecCtx;
    AVCodec *codec;
    AVFrame *pFrame,*pFrameYUV;
    uint8_t *outbuffer;
    AVPacket *packet;
    int ret,got_picture;

    int texture_w,texture_h;
    SDL_Window *screen;
    SDL_Renderer *render;
    SDL_Texture *texture;
    SDL_Thread *video_thread;
    SDL_Event event;
    SDL_Rect prect;

    struct SwsContext *img_convert_ctx;

    char filepath[] = "F:/1.wmv";
    av_register_all();
    avformat_network_init();
    inputctx = avformat_alloc_context();

    if(avformat_open_input(&inputctx,filepath,NULL,NULL)!=0)
    {
        qDebug()<<"打開視頻流失敗";
        return;
    }

    if(avformat_find_stream_info(inputctx,NULL)<0)
    {
        qDebug()<<"獲取文件信息失敗";
        return;
    }

    videoindex = -1;
    for(i = 0;i < inputctx->nb_streams;i++)
    {
        if(inputctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoindex = i;
            break;
        }

    }

    if(videoindex == -1)
    {
       qDebug()<<"沒有發現視頻流";
       return;
    }

    pCodecCtx = inputctx->streams[videoindex]->codec;
    codec  = avcodec_find_decoder(pCodecCtx->codec_id);
    if(NULL == codec)
    {
        qDebug()<<"沒有查到解碼器";
        return;
    }

    if(avcodec_open2(pCodecCtx,codec,NULL)<0)
    {
        qDebug()<<"打開解碼器失敗";
        return;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();


    outbuffer = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
    avpicture_fill((AVPicture *)pFrameYUV,outbuffer,PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);

    img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height
                                     ,PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);



    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER))
    {
        qDebug()<<"初始化sdl失敗";
        return;
    }

    texture_w = pCodecCtx->width;
    texture_h = pCodecCtx->height;

    screen = SDL_CreateWindowFrom((void *)label->winId());
    if(!screen)
    {
        qDebug()<<"創建窗口失敗";
        return;
    }

    render = SDL_CreateRenderer(screen,-1,0);
    texture = SDL_CreateTexture(render,SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width,pCodecCtx->height);

    prect.x = 0;
    prect.y = 0;
    prect.w = pCodecCtx->width;
    prect.h = pCodecCtx->height;

    packet=(AVPacket *)av_malloc(sizeof(AVPacket));
    video_thread = SDL_CreateThread(wait_thread,NULL,NULL);

    while(1)
    {
        SDL_WaitEvent(&event);
        if(event.type==SFM_REFRESH_EVENT)
        {
            if(av_read_frame(inputctx, packet)>=0)
            {
                if(packet->stream_index==videoindex)
                {
                    ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
                    if(ret < 0)
                    {
                        qDebug()<<"解碼失敗";
                        return ;
                    }

                    if(got_picture)
                    {
                        sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

                        SDL_UpdateTexture(texture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0] );
                        SDL_RenderClear(render);

                        SDL_RenderCopy(render, texture, NULL, NULL);
                        SDL_RenderPresent(render);

                    }
                }
                av_free_packet(packet);
            }
            else
            {
                thread_exit=1;
            }

        }
        else if(event.type==SDL_QUIT)
        {
            thread_exit=1;

        }
        else if(event.type==SFM_BREAK_EVENT)
        {
            break;
        }
    }

    SDL_DestroyWindow(screen);
    SDL_Quit();
    delete label;

    isplay = false;
    label = new QLabel(this);
    label->setGeometry(rect);
    label->show();

    av_frame_free(&pFrameYUV);
    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&inputctx);

}

void Widget::pause_play()
{
    if(ui->pause_Button->text() == "暫停")
    {
        thread_pause= 1;
        ui->pause_Button->setText("繼續");
    }
    else
    {
        thread_pause= 0;
        ui->pause_Button->setText("暫停");
    }

}

void Widget::stop_play()
{
    thread_exit=1;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章