一、实现目的
因为一些视频动态库,如大华,必须在播放后才能得到视频的总时长、宽高。这就导致在嵌入到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;
}