通过解析MP4视频文件获取总时长、宽高

一、实现目的

        因为一些视频动态库,如大华,必须在播放后才能得到视频的总时长、宽高。这就导致在嵌入到Qt widget的情况下,刚开始播放那一秒无法准确设置窗口长宽比,所以需要提前获取视频信息。

二、实现原理

        MP4的box描述了媒体播放必需的元数据。直接打开文件,并读取相应的box信息,即可获取所需数据。

        当然不仅仅可以获取总时长、宽高,还可以获取默认音量、播放速度倍率等信息,下面代码就不具体实现了。

三、代码实现

bool getVideoSize(QString fileName, uint64_t& totalTime, int& width, int& height)
{
    if (QFileInfo(fileName).suffix().toLower() != "mp4") {
        return false;
    }

    uint32_t timescale  = 0;
    uint64_t duration   = 0;
    uint32_t tempWidth  = 0;
    uint32_t tempHeight = 0;

    QFile file(fileName);
    if (file.open(QFile::ReadOnly)) {
        uint32_t          seekPos = 0;
        QVector<uint32_t> trakPosVector;

        do {
            uint8_t buff[8] = {0};
            if (file.read((char*)buff, 8) != 8) {
                break;
            }
            uint32_t boxLength = 0;
            for (int i = 0; i < 4; ++i) {
                boxLength += ((uint32_t)buff[i] << ((3 - i) * 8));
            }
            char boxName[5] = {0};
            memcpy(boxName, buff + 4, 4);
            QString tempName(boxName);
            if (tempName == "mvhd") { //解析mvhd,获取时长
                uint32_t tempDataSize = boxLength - 8;
                uint8_t* tempData     = new uint8_t[tempDataSize];
                if (file.read((char*)tempData, tempDataSize) == tempDataSize) {
                    if (tempData[0] == 0) { //版本0
                        for (int i = 0; i < 4; ++i) {
                            timescale += ((uint32_t)tempData[12 + i] << ((3 - i) * 8)); // 4个字节
                            duration += ((uint32_t)tempData[16 + i] << ((3 - i) * 8));  // 4个字节
                        }
                    } else {
                        for (int i = 0; i < 8; ++i) {
                            if (i < 4) {
                                timescale += ((uint32_t)tempData[20 + i] << ((3 - i) * 8)); // 4个字节
                            }
                            duration += ((uint32_t)tempData[24 + i] << ((7 - i) * 8)); // 8个字节
                        }
                    }
                    if (duration != 0 && timescale != 0)
                        totalTime = duration * 1000 / timescale; //单位ms
                }
                delete[] tempData;
            }
            if (tempName == "trak") {
                trakPosVector.append(seekPos + 8); //记录trak位置,可能有多个trak
            }
            if (tempName == "tkhd") { //解析tkhd,获取宽和高
                uint32_t tempDataSize = boxLength - 8;
                uint8_t* tempData     = new uint8_t[tempDataSize];
                if (file.read((char*)tempData, tempDataSize) == tempDataSize) {
                    tempWidth  = ((uint32_t)tempData[tempDataSize - 8] << 8) + (uint32_t)tempData[tempDataSize - 7];
                    tempHeight = ((uint32_t)tempData[tempDataSize - 4] << 8) + (uint32_t)tempData[tempDataSize - 3];
                }
                delete[] tempData;
                if (tempWidth != 0 && tempHeight != 0) { //解析结束
                    width  = (int)tempWidth;
                    height = (int)tempHeight;
                    break;
                }
                if (trakPosVector.isEmpty()) { //解析结束
                    break;
                }
            }
            if (tempName == "moov") {
                seekPos += 8; //跳转至下一层box
            } else {
                seekPos += boxLength; //本层循环
            }
            file.seek(seekPos);

            if (file.atEnd()) {
                if (!trakPosVector.isEmpty()) {
                    file.seek(trakPosVector.at(0)); //跳转至tkhd层
                    trakPosVector.removeFirst();
                }
            }
        } while (!file.atEnd());

        file.close();
    }
    if (duration != 0 && timescale != 0 && tempWidth != 0 && tempHeight != 0) {
        return true;
    }
    return false;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MagicalCat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值