diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 8a62fd51a7..32c550e445 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -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); diff --git a/Telegram/SourceFiles/media/media_audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp index b9301e413c..bb937e17c7 100644 --- a/Telegram/SourceFiles/media/media_audio.cpp +++ b/Telegram/SourceFiles/media/media_audio.cpp @@ -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 videoData, int64 position) { +void Mixer::play( + const AudioMsgId &audio, + std::unique_ptr 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 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(); diff --git a/Telegram/SourceFiles/media/media_audio.h b/Telegram/SourceFiles/media/media_audio.h index 34778981bc..667473db95 100644 --- a/Telegram/SourceFiles/media/media_audio.h +++ b/Telegram/SourceFiles/media/media_audio.h @@ -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 videoData, int64 position = 0); + void play(const AudioMsgId &audio, TimeMs positionMs = 0); + void play(const AudioMsgId &audio, std::unique_ptr 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(); diff --git a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp index fec669f74f..d76fbd9680 100644 --- a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp @@ -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) { } } } diff --git a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h index a66ff539a8..17d302fb79 100644 --- a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h +++ b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h @@ -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; diff --git a/Telegram/SourceFiles/media/media_audio_loader.h b/Telegram/SourceFiles/media/media_audio_loader.h index 5835b94470..d118bebd57 100644 --- a/Telegram/SourceFiles/media/media_audio_loader.h +++ b/Telegram/SourceFiles/media/media_audio_loader.h @@ -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; diff --git a/Telegram/SourceFiles/media/media_audio_loaders.cpp b/Telegram/SourceFiles/media/media_audio_loaders.cpp index ccd6e72585..39bafd6295 100644 --- a/Telegram/SourceFiles/media/media_audio_loaders.cpp +++ b/Telegram/SourceFiles/media/media_audio_loaders.cpp @@ -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; diff --git a/Telegram/SourceFiles/media/media_audio_loaders.h b/Telegram/SourceFiles/media/media_audio_loaders.h index e3d7dfc719..0f506bce74 100644 --- a/Telegram/SourceFiles/media/media_audio_loaders.h +++ b/Telegram/SourceFiles/media/media_audio_loaders.h @@ -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); }; diff --git a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp index a586a2b438..04fbd11005 100644 --- a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp @@ -37,7 +37,7 @@ ChildFFMpegLoader::ChildFFMpegLoader(std::unique_ptr &&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); diff --git a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h index fdeea40b86..144e782607 100644 --- a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h +++ b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h @@ -85,7 +85,7 @@ class ChildFFMpegLoader : public AudioPlayerLoader { public: ChildFFMpegLoader(std::unique_ptr &&data); - bool open(qint64 &position) override; + bool open(TimeMs positionMs) override; bool check(const FileLocation &file, const QByteArray &data) override { return true; diff --git a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp index ebd47d3dbd..42e78dbc69 100644 --- a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp +++ b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp @@ -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; } } diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp index 7c5a1fc3f3..6b45a16ebb 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.cpp +++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp @@ -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); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 1afb996958..eb91077fe9 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -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);