Always specify seek position in TimeMs.

This way it won't rely on the sample rate of the audio file.

Fixes #4139.
This commit is contained in:
John Preston 2017-12-10 12:52:38 +04:00
parent 9fff2bf4c7
commit 4ef3de5287
13 changed files with 95 additions and 69 deletions

View File

@ -1707,7 +1707,9 @@ void HistoryDocument::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool
auto state = Media::Player::mixer()->currentState(type);
if (state.id == AudioMsgId(_data, _parent->fullId()) && state.length) {
auto currentProgress = voice->seekingCurrent();
auto currentPosition = qRound(currentProgress * state.length);
auto currentPosition = state.frequency
? qRound(currentProgress * state.length * 1000. / state.frequency)
: 0;
Media::Player::mixer()->seek(type, currentPosition);
voice->ensurePlayback(this);

View File

@ -619,12 +619,15 @@ bool Mixer::fadedStop(AudioMsgId::Type type, bool *fadedStart) {
return false;
}
void Mixer::play(const AudioMsgId &audio, int64 position) {
void Mixer::play(const AudioMsgId &audio, TimeMs positionMs) {
setSongVolume(Global::SongVolume());
play(audio, nullptr, position);
play(audio, nullptr, positionMs);
}
void Mixer::play(const AudioMsgId &audio, std::unique_ptr<VideoSoundData> videoData, int64 position) {
void Mixer::play(
const AudioMsgId &audio,
std::unique_ptr<VideoSoundData> videoData,
TimeMs positionMs) {
Expects(!videoData || audio.playId() != 0);
auto type = audio.type();
@ -700,10 +703,11 @@ void Mixer::play(const AudioMsgId &audio, std::unique_ptr<VideoSoundData> videoD
auto newState = (type == AudioMsgId::Type::Song) ? State::Stopped : State::StoppedAtError;
setStoppedState(current, newState);
} else {
current->state.position = position;
current->state.position = (positionMs * current->state.frequency)
/ 1000LL;
current->state.state = current->videoData ? State::Paused : fadedStart ? State::Starting : State::Playing;
current->loading = true;
emit loaderOnStart(current->state.id, position);
emit loaderOnStart(current->state.id, positionMs);
if (type == AudioMsgId::Type::Voice) {
emit suppressSong();
}
@ -871,20 +875,31 @@ void Mixer::resume(const AudioMsgId &audio, bool fast) {
if (current) emit updated(current);
}
void Mixer::seek(AudioMsgId::Type type, int64 position) {
void Mixer::seek(AudioMsgId::Type type, TimeMs positionMs) {
QMutexLocker lock(&AudioMutex);
auto current = trackForType(type);
auto audio = current->state.id;
const auto current = trackForType(type);
const auto audio = current->state.id;
Audio::AttachToDevice();
auto streamCreated = current->isStreamCreated();
auto fastSeek = (position >= current->bufferedPosition && position < current->bufferedPosition + current->bufferedLength - (current->loaded ? 0 : kDefaultFrequency));
if (!streamCreated) {
fastSeek = false;
} else if (IsStoppedOrStopping(current->state.state)) {
fastSeek = false;
}
const auto streamCreated = current->isStreamCreated();
const auto position = (positionMs * current->frequency) / 1000LL;
const auto fastSeek = [&] {
const auto loadedStart = current->bufferedPosition;
const auto loadedLength = current->bufferedLength;
const auto skipBack = (current->loaded ? 0 : kDefaultFrequency);
const auto availableEnd = loadedStart + loadedLength - skipBack;
if (position < loadedStart) {
return false;
} else if (position >= availableEnd) {
return false;
} else if (!streamCreated) {
return false;
} else if (IsStoppedOrStopping(current->state.state)) {
return false;
}
return true;
}();
if (fastSeek) {
alSourcei(current->stream.source, AL_SAMPLE_OFFSET, position - current->bufferedPosition);
if (!checkCurrentALError(type)) return;
@ -921,7 +936,7 @@ void Mixer::seek(AudioMsgId::Type type, int64 position) {
case State::StoppedAtError:
case State::StoppedAtStart: {
lock.unlock();
} return play(audio, position);
} return play(audio, positionMs);
}
emit faderOnTimer();
}
@ -1386,8 +1401,8 @@ public:
FFMpegAttributesReader(const FileLocation &file, const QByteArray &data) : AbstractFFMpegLoader(file, data, base::byte_vector()) {
}
bool open(qint64 &position) override {
if (!AbstractFFMpegLoader::open(position)) {
bool open(TimeMs positionMs) override {
if (!AbstractFFMpegLoader::open(positionMs)) {
return false;
}
@ -1487,8 +1502,8 @@ namespace Player {
FileLoadTask::Song PrepareForSending(const QString &fname, const QByteArray &data) {
auto result = FileLoadTask::Song();
FFMpegAttributesReader reader(FileLocation(fname), data);
qint64 position = 0;
if (reader.open(position) && reader.samplesCount() > 0) {
const auto positionMs = TimeMs(0);
if (reader.open(positionMs) && reader.samplesCount() > 0) {
result.duration = reader.samplesCount() / reader.samplesFrequency();
result.title = reader.title();
result.performer = reader.performer();
@ -1505,8 +1520,8 @@ public:
FFMpegWaveformCounter(const FileLocation &file, const QByteArray &data) : FFMpegLoader(file, data, base::byte_vector()) {
}
bool open(qint64 &position) override {
if (!FFMpegLoader::open(position)) {
bool open(TimeMs positionMs) override {
if (!FFMpegLoader::open(positionMs)) {
return false;
}
@ -1584,8 +1599,8 @@ private:
VoiceWaveform audioCountWaveform(const FileLocation &file, const QByteArray &data) {
FFMpegWaveformCounter counter(file, data);
qint64 position = 0;
if (counter.open(position)) {
const auto positionMs = TimeMs(0);
if (counter.open(positionMs)) {
return counter.waveform();
}
return VoiceWaveform();

View File

@ -121,11 +121,11 @@ class Mixer : public QObject, private base::Subscriber {
public:
Mixer();
void play(const AudioMsgId &audio, int64 position = 0);
void play(const AudioMsgId &audio, std::unique_ptr<VideoSoundData> videoData, int64 position = 0);
void play(const AudioMsgId &audio, TimeMs positionMs = 0);
void play(const AudioMsgId &audio, std::unique_ptr<VideoSoundData> videoData, TimeMs positionMs = 0);
void pause(const AudioMsgId &audio, bool fast = false);
void resume(const AudioMsgId &audio, bool fast = false);
void seek(AudioMsgId::Type type, int64 position); // type == AudioMsgId::Type::Song
void seek(AudioMsgId::Type type, TimeMs positionMs); // type == AudioMsgId::Type::Song
void stop(const AudioMsgId &audio);
void stop(const AudioMsgId &audio, State state);
@ -165,7 +165,7 @@ private slots:
signals:
void updated(const AudioMsgId &audio);
void stoppedOnError(const AudioMsgId &audio);
void loaderOnStart(const AudioMsgId &audio, qint64 position);
void loaderOnStart(const AudioMsgId &audio, qint64 positionMs);
void loaderOnCancel(const AudioMsgId &audio);
void faderOnTimer();

View File

@ -37,7 +37,7 @@ bool IsPlanarFormat(int format) {
} // namespace
bool AbstractFFMpegLoader::open(qint64 &position) {
bool AbstractFFMpegLoader::open(TimeMs positionMs) {
if (!AudioPlayerLoader::openFile()) {
return false;
}
@ -192,8 +192,8 @@ FFMpegLoader::FFMpegLoader(const FileLocation &file, const QByteArray &data, bas
frame = av_frame_alloc();
}
bool FFMpegLoader::open(qint64 &position) {
if (!AbstractFFMpegLoader::open(position)) {
bool FFMpegLoader::open(TimeMs positionMs) {
if (!AbstractFFMpegLoader::open(positionMs)) {
return false;
}
@ -295,7 +295,6 @@ bool FFMpegLoader::open(qint64 &position) {
sampleSize = AudioToChannels * sizeof(short);
_samplesFrequency = dstRate;
_samplesCount = av_rescale_rnd(_samplesCount, dstRate, srcRate, AV_ROUND_UP);
position = av_rescale_rnd(position, dstRate, srcRate, AV_ROUND_DOWN);
fmt = AL_FORMAT_STEREO16;
maxResampleSamples = av_rescale_rnd(AVBlockSize / sampleSize, dstRate, srcRate, AV_ROUND_UP);
@ -304,10 +303,12 @@ bool FFMpegLoader::open(qint64 &position) {
return false;
}
}
if (position) {
int64 ts = (position * fmtContext->streams[streamId]->time_base.den) / (_samplesFrequency * fmtContext->streams[streamId]->time_base.num);
if (av_seek_frame(fmtContext, streamId, ts, AVSEEK_FLAG_ANY) < 0) {
if (av_seek_frame(fmtContext, streamId, ts, 0) < 0) {
if (positionMs) {
const auto timeBase = fmtContext->streams[streamId]->time_base;
const auto timeStamp = (positionMs * timeBase.den)
/ (1000LL * timeBase.num);
if (av_seek_frame(fmtContext, streamId, timeStamp, AVSEEK_FLAG_ANY) < 0) {
if (av_seek_frame(fmtContext, streamId, timeStamp, 0) < 0) {
}
}
}

View File

@ -37,7 +37,7 @@ public:
AbstractFFMpegLoader(const FileLocation &file, const QByteArray &data, base::byte_vector &&bytes) : AudioPlayerLoader(file, data, std::move(bytes)) {
}
bool open(qint64 &position) override;
bool open(TimeMs positionMs) override;
int64 samplesCount() override {
return _samplesCount;
@ -75,7 +75,7 @@ class FFMpegLoader : public AbstractFFMpegLoader {
public:
FFMpegLoader(const FileLocation &file, const QByteArray &data, base::byte_vector &&bytes);
bool open(qint64 &position) override;
bool open(TimeMs positionMs) override;
int32 format() override {
return fmt;

View File

@ -31,7 +31,7 @@ public:
virtual bool check(const FileLocation &file, const QByteArray &data);
virtual bool open(qint64 &position) = 0;
virtual bool open(TimeMs positionMs) = 0;
virtual int64 samplesCount() = 0;
virtual int32 samplesFrequency() = 0;
virtual int32 format() = 0;

View File

@ -98,7 +98,7 @@ void Loaders::clearFromVideoQueue() {
void Loaders::onInit() {
}
void Loaders::onStart(const AudioMsgId &audio, qint64 position) {
void Loaders::onStart(const AudioMsgId &audio, qint64 positionMs) {
auto type = audio.type();
clear(type);
{
@ -111,7 +111,7 @@ void Loaders::onStart(const AudioMsgId &audio, qint64 position) {
track->loading = true;
}
loadData(audio, position);
loadData(audio, positionMs);
}
AudioMsgId Loaders::clear(AudioMsgId::Type type) {
@ -133,13 +133,13 @@ void Loaders::emitError(AudioMsgId::Type type) {
}
void Loaders::onLoad(const AudioMsgId &audio) {
loadData(audio, 0);
loadData(audio, TimeMs(0));
}
void Loaders::loadData(AudioMsgId audio, qint64 position) {
void Loaders::loadData(AudioMsgId audio, TimeMs positionMs) {
auto err = SetupNoErrorStarted;
auto type = audio.type();
auto l = setupLoader(audio, err, position);
auto l = setupLoader(audio, err, positionMs);
if (!l) {
if (err == SetupErrorAtStart) {
emitError(type);
@ -210,12 +210,13 @@ void Loaders::loadData(AudioMsgId audio, qint64 position) {
return;
}
track->format = l->format();
track->frequency = l->samplesFrequency();
const auto position = (positionMs * track->frequency) / 1000LL;
track->bufferedPosition = position;
track->state.position = position;
track->fadeStartPosition = position;
track->format = l->format();
track->frequency = l->samplesFrequency();
}
if (samplesCount) {
track->ensureStreamCreated();
@ -291,7 +292,10 @@ void Loaders::loadData(AudioMsgId audio, qint64 position) {
}
}
AudioPlayerLoader *Loaders::setupLoader(const AudioMsgId &audio, SetupError &err, qint64 &position) {
AudioPlayerLoader *Loaders::setupLoader(
const AudioMsgId &audio,
SetupError &err,
TimeMs positionMs) {
err = SetupErrorAtStart;
QMutexLocker lock(internal::audioPlayerMutex());
if (!mixer()) return nullptr;
@ -339,7 +343,7 @@ AudioPlayerLoader *Loaders::setupLoader(const AudioMsgId &audio, SetupError &err
}
l = loader->get();
if (!l->open(position)) {
if (!l->open(positionMs)) {
track->state.state = State::StoppedAtStart;
return nullptr;
}
@ -350,7 +354,6 @@ AudioPlayerLoader *Loaders::setupLoader(const AudioMsgId &audio, SetupError &err
}
track->state.length = length;
track->state.frequency = l->samplesFrequency();
if (!track->state.frequency) track->state.frequency = kDefaultFrequency;
err = SetupNoErrorStarted;
} else if (track->loaded) {
err = SetupErrorLoadedFull;

View File

@ -45,7 +45,7 @@ signals:
public slots:
void onInit();
void onStart(const AudioMsgId &audio, qint64 position);
void onStart(const AudioMsgId &audio, qint64 positionMs);
void onLoad(const AudioMsgId &audio);
void onCancel(const AudioMsgId &audio);
@ -72,8 +72,11 @@ private:
SetupErrorLoadedFull = 2,
SetupNoErrorStarted = 3,
};
void loadData(AudioMsgId audio, qint64 position);
AudioPlayerLoader *setupLoader(const AudioMsgId &audio, SetupError &err, qint64 &position);
void loadData(AudioMsgId audio, TimeMs positionMs);
AudioPlayerLoader *setupLoader(
const AudioMsgId &audio,
SetupError &err,
TimeMs positionMs);
Mixer::Track *checkLoader(AudioMsgId::Type type);
};

View File

@ -37,7 +37,7 @@ ChildFFMpegLoader::ChildFFMpegLoader(std::unique_ptr<VideoSoundData> &&data) : A
_frame = av_frame_alloc();
}
bool ChildFFMpegLoader::open(qint64 &position) {
bool ChildFFMpegLoader::open(TimeMs positionMs) {
int res = 0;
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
@ -106,7 +106,6 @@ bool ChildFFMpegLoader::open(qint64 &position) {
_sampleSize = AudioToChannels * sizeof(short);
_parentData->frequency = _dstRate;
_parentData->length = av_rescale_rnd(_parentData->length, _dstRate, _srcRate, AV_ROUND_UP);
position = av_rescale_rnd(position, _dstRate, _srcRate, AV_ROUND_DOWN);
_format = AL_FORMAT_STEREO16;
_maxResampleSamples = av_rescale_rnd(AVBlockSize / _sampleSize, _dstRate, _srcRate, AV_ROUND_UP);

View File

@ -85,7 +85,7 @@ class ChildFFMpegLoader : public AudioPlayerLoader {
public:
ChildFFMpegLoader(std::unique_ptr<VideoSoundData> &&data);
bool open(qint64 &position) override;
bool open(TimeMs positionMs) override;
bool check(const FileLocation &file, const QByteArray &data) override {
return true;

View File

@ -400,9 +400,11 @@ bool FFMpegReaderImplementation::start(Mode mode, TimeMs &positionMs) {
}
}
if (positionMs > 0) {
auto ts = (positionMs * _fmtContext->streams[_streamId]->time_base.den) / (1000LL * _fmtContext->streams[_streamId]->time_base.num);
if (av_seek_frame(_fmtContext, _streamId, ts, 0) < 0) {
if (av_seek_frame(_fmtContext, _streamId, ts, AVSEEK_FLAG_BACKWARD) < 0) {
const auto timeBase = _fmtContext->streams[_streamId]->time_base;
const auto timeStamp = (positionMs * timeBase.den)
/ (1000LL * timeBase.num);
if (av_seek_frame(_fmtContext, _streamId, timeStamp, 0) < 0) {
if (av_seek_frame(_fmtContext, _streamId, timeStamp, AVSEEK_FLAG_BACKWARD) < 0) {
return false;
}
}
@ -415,8 +417,7 @@ bool FFMpegReaderImplementation::start(Mode mode, TimeMs &positionMs) {
}
if (hasAudio()) {
auto position = (positionMs * soundData->frequency) / 1000LL;
Player::mixer()->play(_audioMsgId, std::move(soundData), position);
Player::mixer()->play(_audioMsgId, std::move(soundData), positionMs);
}
if (readResult == PacketResult::Ok) {
@ -428,9 +429,11 @@ bool FFMpegReaderImplementation::start(Mode mode, TimeMs &positionMs) {
bool FFMpegReaderImplementation::inspectAt(TimeMs &positionMs) {
if (positionMs > 0) {
auto ts = (positionMs * _fmtContext->streams[_streamId]->time_base.den) / (1000LL * _fmtContext->streams[_streamId]->time_base.num);
if (av_seek_frame(_fmtContext, _streamId, ts, 0) < 0) {
if (av_seek_frame(_fmtContext, _streamId, ts, AVSEEK_FLAG_BACKWARD) < 0) {
const auto timeBase = _fmtContext->streams[_streamId]->time_base;
const auto timeStamp = (positionMs * timeBase.den)
/ (1000LL * timeBase.num);
if (av_seek_frame(_fmtContext, _streamId, timeStamp, 0) < 0) {
if (av_seek_frame(_fmtContext, _streamId, timeStamp, AVSEEK_FLAG_BACKWARD) < 0) {
return false;
}
}

View File

@ -173,8 +173,8 @@ void CoverWidget::handleSeekFinished(float64 progress) {
auto type = AudioMsgId::Type::Song;
auto state = Media::Player::mixer()->currentState(type);
if (state.id && state.length) {
Media::Player::mixer()->seek(type, qRound(progress * state.length));
if (state.id && state.length && state.frequency) {
Media::Player::mixer()->seek(type, qRound(progress * state.length * 1000. / state.frequency));
}
instance()->stopSeeking(type);

View File

@ -248,8 +248,8 @@ void Widget::handleSeekFinished(float64 progress) {
_seekPositionMs = -1;
auto state = mixer()->currentState(_type);
if (state.id && state.length) {
mixer()->seek(_type, qRound(progress * state.length));
if (state.id && state.length && state.frequency) {
mixer()->seek(_type, qRound(progress * state.length * 1000. / state.frequency));
}
instance()->stopSeeking(_type);