調用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;
}