从零实现简易播放器-1.模拟视频播放

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shizheng163/article/details/85085300

模拟视频播放

作者:史正
邮箱:[email protected]
如有错误还请及时指正
如果有错误的描述给您带来不便还请见谅
如需交流请发送邮件,欢迎联系

简述

我们知道,视频是由连续的图片组成的(见上一节:音视频基本概念), 所以这里我们先用Qt创建一个简易的视频播放界面, 读取一个文件夹,顺序播放其中的图片达到模拟视频播放的效果。

没有QT开发应用程序基础的同学建议先学习下QT应用程序开发

这里推荐一个介绍QT开发比较全的博客:

Qt基础教程之Qt学习之路

这一节的完整代码:

注意:

  • 前期所有代码都是在Windows下开发的
  • 直接渲染Jpeg等图片格式, 时间开销特别大, 单张图片超过40ms,导致播放缓慢, 正确做法应该使用opengl渲染yuv或者rgb格式的图片, 这一点以后章节有示例。

另外推荐几个界面图标素材网站

界面搭建

这一部分我们省略掉, 通过QTUI设计界面就可以搭建简易的播放界面。

界面主要由以下两部分组成:

  1. 打开文件夹按钮
  2. 显示图片的Label

使用ffmpeg获取连续的图片

ffmpeg是一个用C语言编写的处理音视频的开源库, 我们平时所见的播放器几乎都是通过ffmpeg去编写的。

这里给出一条命令, 可以通过ffmpeg将一段视频拆分成连续的图片:

  • ffmpeg -i Suger.mp4 -b 3000k -ss 00:00:10 -t 10 SugerFrames/Frame_%04d.jpeg

这里-i 是输出 -b 是码率, 码率过低的话会导致画面清晰度降低, -ss 是开始时间, -t 是持续时间, 最后一个是保存位置,%04d标明编号为4位, 从1开始, 不足4位前补0。

ffmpeg可程序程序下载可通过官方网站:

http://ffmpeg.org/download.html

选择文件夹,获取图片文件名列表

核心代码如下,如果目录中有中文名, 使用std::cout或者printf打印会有中文乱码, 从网上找了好多方法都没找到非qDebug()的打印可以避免这种情况, 如果你有好的解决方法请及时告知, 多谢!

    string srcPath = dir;
    dir.append("/*.*");
    dir = QString::fromStdString(dir).replace("/", "\\\\").toStdString();
    std::vector<std::string> pictures;
    struct _finddata_t fileinfo;

    intptr_t fileIdx = _findfirst(dir.c_str(), &fileinfo);
    qDebug() << "search dir:" << dir.c_str();
    if(fileIdx != -1)
    {
        do
        {
            if(!(fileinfo.attrib & _A_SUBDIR))
            {
                string filename(fileinfo.name);
                size_t pos = filename.find_last_of('.');
                if(pos != std::string::npos)
                {
                    string extension = filename.substr(pos+1);
                    std::transform(extension.begin(), extension.end(), extension.begin(), std::ptr_fun<int, int>(tolower));
                    if(extension == "img" || extension == "png" || extension == "jpeg" || extension == "jpg")
                    {
                        qDebug() << "get picture:" << fileinfo.name;
                        pictures.push_back(srcPath + "/" + filename);
                    }
                }
            }
        }while(_findnext(fileIdx, &fileinfo) == 0);
    }
    else
        qDebug() << "could not find file from" << dir.c_str();
    _findclose(fileIdx);
    return pictures;

显示图片

这里是一个线程读取图片另一个线程在QLabel上渲染图片

注意:

不能在非UI线程改变去修改界面,但是这里使用的是QLabel的`setPixmap`。
这里没去查询原因,但估计应该是setPixmap是修改了显示位置中内存的值,并没有更新UI。
如有错误,请及时指出,谢谢!
std::mutex mutexForMemoryPictures;
std::queue<PicturePtr> memoryPictures;
std::condition_variable conditionVar;
unsigned totolPictureNum = pictureVector.size();
unsigned curPictureNum = 0;
//禁用打开图标,用信号槽去更新UI
emit SignalBtnEnable(false);
std::thread([&](std::vector<std::string> pictures){
    for(string pictureName : pictures)
    {
        FILE * fPict = fopen(pictureName.c_str(), "rb");
        fseek(fPict, 0, SEEK_END);
        PicturePtr pPicture(new Picture);
        pPicture->m_len = ftell(fPict);
        pPicture->m_pData = new uint8_t[pPicture->m_len];
        pPicture->m_strPictureName = pictureName;
        fseek(fPict, 0, SEEK_SET);
        fread(pPicture->m_pData, sizeof(uint8_t), pPicture->m_len, fPict);
        fclose(fPict);
        unique_lock<mutex> locker(mutexForMemoryPictures);
        memoryPictures.push(pPicture);
        conditionVar.notify_one();
    }
}, pictureVector).detach();
QImage *pimg = new QImage;
while(curPictureNum < totolPictureNum)
{
    unique_lock<mutex> locker(mutexForMemoryPictures);
    if(memoryPictures.empty())
        conditionVar.wait(locker);
    PicturePtr ptr = memoryPictures.front();
    memoryPictures.pop();
    locker.unlock();
    curPictureNum++;
    if(!pimg->loadFromData(ptr->m_pData, ptr->m_len, "jpg"))
    {
        qDebug() << "error: load memory picture" << ptr->m_strPictureName.c_str();
    }
    //渲染图片
    ui->m_pLabVideoImage->setPixmap(QPixmap::fromImage(pimg->scaled(ui->m_pLabVideoImage->size())));
    string textName(ptr->m_strPictureName);
    textName.append("\t\t");
    textName.append(to_string(curPictureNum));
    textName.append("/");
    textName.append(to_string(totolPictureNum));
    //进度条显示文本:当前显示到哪张图片
    ui->m_pLabProcessBar->setText(textName.c_str());
}
delete pimg;
//重置界面显示
this->ResetControls();
}

猜你喜欢

转载自blog.csdn.net/shizheng163/article/details/85085300