'Qt wrong duration with variable bitrate
QMediaPlayer calculates wrong duration with variable bitrate mp3. Yes, I know a similar topic was already opened, but is pretty old (2012). In addition, both VLC and Clementine, using Qt, for the same mp3 files can calculate the exact duration. So, I do not think it's a Qt bug.
I take the mp3 duration in this way:
void MainWindow::playerOnMediaStatusChanged(QMediaPlayer::MediaStatus status) {
if (status == QMediaPlayer::BufferedMedia) {
qint64 duration = player->duration(); //wrong
}
}
Can you help me?
Solution 1:[1]
QtMultiMedia::QMediaPlayer can not recognize variable bitrate mode.
It will give you wrong duration and wrong position.
You can patch QMediaPlayer to calculate the real position/duration.
Python/PySide2 example:
# Created by [email protected] at 2022/2/22 21:39
import taglib
from IceSpringPathLib import Path
from PySide2 import QtMultimedia, QtCore
class PatchedMediaPlayer(QtMultimedia.QMediaPlayer):
durationChanged: QtCore.SignalInstance = QtCore.Signal(int)
positionChanged: QtCore.SignalInstance = QtCore.Signal(int)
def __init__(self):
super().__init__()
self._realDuration = 0
self._lastFakePosition = 0
self._bugRate = 0.0
super().durationChanged.connect(self._onSuperDurationChanged)
super().positionChanged.connect(self._onSuperPositionChanged)
def _onSuperDurationChanged(self, duration: int):
self._bugRate = duration / self._realDuration
self.durationChanged.emit(self._realDuration)
def _onSuperPositionChanged(self):
self.positionChanged.emit(self.position())
def setMedia(self, media: QtMultimedia.QMediaContent, stream: QtCore.QIODevice = None, realDuration=None) -> None:
self.blockSignals(True)
super().setMedia(media, stream)
self.blockSignals(False)
self._realDuration = realDuration
self._lastFakePosition = 0
self._bugRate = 0.0
if realDuration is None:
filename = media.canonicalUrl().toLocalFile()
file = taglib.File(filename)
bitrate = file.bitrate
file.close()
self._realDuration = Path(filename).stat().st_size * 8 // bitrate
def setPosition(self, position: int) -> None:
assert self._bugRate != 0 or position == 0
fakePosition = int(position * self._bugRate)
super().setPosition(int(position * self._bugRate))
self._lastFakePosition = fakePosition
def duration(self) -> int:
return self._realDuration
def position(self) -> int:
elapsed = super().position() - self._lastFakePosition
lastPosition = 0 if self._lastFakePosition == 0 else int(self._lastFakePosition / self._bugRate)
realPosition = lastPosition + elapsed
realPosition = max(realPosition, 0)
realPosition = min(realPosition, self._realDuration)
return realPosition
Requirement: pip install pytaglib==1.4.6, or any other library to fetch the real bitrate
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | BaiJiFeiLong |
