1. 程式人生 > 其它 >(轉載)FFMPEG一個最簡單的視訊播放器

(轉載)FFMPEG一個最簡單的視訊播放器

#pragma once
#include <QThread>
#include <QImage>
class VideoPlayer :public QThread
{
    Q_OBJECT
public:
    VideoPlayer();
    ~VideoPlayer();
    void run();
private:
    // 延時函式
    void delay(int msec);
signals:
    void sig_GetOneFrame(QImage);
};
#include "VideoPlayer.h"
#include 
<QCoreApplication> #include <QTime> #include <iostream> #include <QDebug> // 呼叫FFmpeg的標頭檔案 extern "C"{ #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavdevice/avdevice.h" #include "libavutil/pixfmt.h
" } using namespace std; VideoPlayer::VideoPlayer() { } VideoPlayer::~VideoPlayer() { } void VideoPlayer::run() { //1.初始化ffmpeg av_register_all(); //初始化FFMPEG 呼叫了這個才能正常適用編碼器和解碼器 avformat_network_init(); // 初始化網路模組 //=========================== 建立AVFormatContext結構體 ===============================
// //分配一個AVFormatContext,FFMPEG所有的操作都要通過這個AVFormatContext來進行 AVFormatContext *pFormatCtx = avformat_alloc_context(); //==================================== 開啟檔案 ======================================// char *file_path = "./2.mp4" ;//這裡必須使用左斜槓 int ret = avformat_open_input(&pFormatCtx, file_path, NULL, NULL); if (ret != 0) { qDebug() << "open error!"; return; } //迴圈查詢視訊中包含的流資訊,直到找到視訊型別的流 //便將其記錄下來 儲存到videoStream變數中 int i; int videoStream; //=================================== 獲取視訊流資訊 ===================================// if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { qDebug() << "Could't find stream infomation."; return; } videoStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; } } //如果videoStream為-1 說明沒有找到視訊流 if (videoStream == -1) { qDebug() << "Didn't find a video stream."; return; } //================================= 查詢解碼器 ===================================// AVCodecContext* pCodecCtx = pFormatCtx->streams[videoStream]->codec; pCodecCtx->time_base.num = 1; pCodecCtx->time_base.den = 25; AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) { qDebug() << "Codec not found."; return; } //================================ 開啟解碼器 ===================================// if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)// 具體採用什麼解碼器ffmpeg經過封裝 我們無須知道 { qDebug() << "Could not open codec."; return; } //================================ 設定資料轉換引數 ================================// SwsContext * img_convert_ctx; img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, //源地址長寬以及資料格式 pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, //目的地址長寬以及資料格式 SWS_BICUBIC, NULL, NULL, NULL);//演算法型別 AV_PIX_FMT_YUVJ420P AV_PIX_FMT_BGR24 //與圖片不相同 //img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, //源地址長寬以及資料格式 // pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUVJ420P, //目的地址長寬以及資料格式 // SWS_BICUBIC, NULL, NULL, NULL);//演算法型別 AV_PIX_FMT_YUVJ420P AV_PIX_FMT_BGR24 //==================================== 分配空間 ==================================// //一幀影象資料大小 int numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height); //int numBytes = avpicture_get_size(AV_PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height);//與圖片不相同的 unsigned char *out_buffer; out_buffer = (unsigned char *)av_malloc(numBytes * sizeof(unsigned char)); AVFrame * pFrame; pFrame = av_frame_alloc(); AVFrame * pFrameRGB; pFrameRGB = av_frame_alloc(); //avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height); avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height); //會將pFrameRGB的資料按RGB格式自動"關聯"到buffer 即pFrameRGB中的資料改變了 out_buffer中的資料也會相應的改變 //=========================== 分配AVPacket結構體 ===============================// int y_size = pCodecCtx->width * pCodecCtx->height; AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket)); //分配一個packet av_new_packet(packet, y_size); //分配packet的資料 //列印輸入和輸出資訊:長度 位元率 流格式等 av_dump_format(pFormatCtx, 0, file_path, 0); //輸出視訊資訊Image)),this,SLOT(slotGetOneFrame(QImage))); // //2.=========================== 讀取視訊資訊 ===============================// int index = 0; //讀取的是一幀視訊 資料存入一個AVPacket的結構中 while (av_read_frame(pFormatCtx, packet) >= 0) { //此時資料儲存在packet中 if (packet && packet->stream_index == videoStream) { //3.視訊解碼函式 解碼之後的資料儲存在 pFrame中 int got_picture = 0; ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if (ret < 0) { cout << "decode error." << endl; return; } //轉換一幀影象 4. YUV->RGB sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, // pFrameRGB->data, pFrameRGB->linesize); //目的 QImage tmpImg((uchar *)out_buffer, pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB32); QImage image = tmpImg.copy(); emit sig_GetOneFrame(image); //傳送訊號 delay(100); } } free(packet); av_frame_free(&pFrameRGB); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_free_context(pFormatCtx); } void VideoPlayer::delay(int msec) { QTime dieTime = QTime::currentTime().addMSecs(msec); while (QTime::currentTime() < dieTime) QCoreApplication::processEvents(QEventLoop::AllEvents, 100); }

private:

VideoPlayer *VideoPlayerThread;

private slots:

void slotGetOneFrame(QImage);

VideoPlayerThread = new VideoPlayer();
connect(VideoPlayerThread, SIGNAL(sig_GetOneFrame(QImage)), this, SLOT(slotGetOneFrame(QImage)));

void FFmpegDemo::on_playerBtn_clicked()
{
    if (VideoPlayerThread != NULL)
    {
        VideoPlayerThread->start();
    }
}

void FFmpegDemo::slotGetOneFrame(QImage img)
{
    ui.label->setPixmap(QPixmap::fromImage(img)); // 在label上播放視訊圖片
}

參考:

https://www.cnblogs.com/linuxAndMcu/p/12046600.html

https://mp.weixin.qq.com/s/tEGF6KJKx9xyY475c30ceQ