(转载)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

 

上一篇:FFMPEG :resource temporarily unavailable


下一篇:wasm 编译 ffmpeg 还有问题版本