ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

(转载)FFMPEG一个最简单的视频播放器

2021-12-01 17:04:05  阅读:188  来源: 互联网

标签:播放器 视频 pCodecCtx FFMPEG VideoPlayer packet height AV include


#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

 

标签:播放器,视频,pCodecCtx,FFMPEG,VideoPlayer,packet,height,AV,include
来源: https://www.cnblogs.com/zeliangzhang/p/15629772.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有