summaryrefslogtreecommitdiffstats
path: root/Telegram/SourceFiles/media/audio/media_audio_loaders.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Telegram/SourceFiles/media/audio/media_audio_loaders.cpp')
-rw-r--r--Telegram/SourceFiles/media/audio/media_audio_loaders.cpp216
1 files changed, 170 insertions, 46 deletions
diff --git a/Telegram/SourceFiles/media/audio/media_audio_loaders.cpp b/Telegram/SourceFiles/media/audio/media_audio_loaders.cpp
index df18e891c1..6fe4fcc313 100644
--- a/Telegram/SourceFiles/media/audio/media_audio_loaders.cpp
+++ b/Telegram/SourceFiles/media/audio/media_audio_loaders.cpp
@@ -13,11 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Media {
namespace Player {
-namespace {
-
-constexpr auto kPlaybackBufferSize = 256 * 1024;
-
-} // namespace
Loaders::Loaders(QThread *thread)
: _fromExternalNotify([=] { videoSoundAdded(); }) {
@@ -151,17 +146,29 @@ void Loaders::onLoad(const AudioMsgId &audio) {
}
void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
- auto err = SetupNoErrorStarted;
auto type = audio.type();
- auto l = setupLoader(audio, err, positionMs);
+ auto setup = setupLoader(audio, positionMs);
+ const auto l = setup.loader;
if (!l) {
- if (err == SetupErrorAtStart) {
+ if (setup.errorAtStart) {
emitError(type);
}
return;
}
- auto started = (err == SetupNoErrorStarted);
+ const auto sampleSize = l->sampleSize();
+ const auto speedChanged = (setup.newSpeed != setup.oldSpeed);
+ auto updatedWithSpeed = speedChanged
+ ? rebufferOnSpeedChange(setup)
+ : std::optional<Mixer::Track::WithSpeed>();
+ if (!speedChanged && setup.oldSpeed > 0.) {
+ const auto normalPosition = Mixer::Track::SpeedIndependentPosition(
+ setup.position,
+ setup.oldSpeed);
+ l->dropFramesTill(normalPosition);
+ }
+
+ const auto started = setup.justStarted;
auto finished = false;
auto waiting = false;
auto errAtStart = started;
@@ -170,10 +177,15 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
auto accumulatedCount = 0;
if (l->holdsSavedDecodedSamples()) {
l->takeSavedDecodedSamples(&accumulated);
- accumulatedCount = accumulated.size() / l->sampleSize();
+ accumulatedCount = accumulated.size() / sampleSize;
}
- while (accumulated.size() < kPlaybackBufferSize) {
+ const auto accumulateTill = l->bytesPerBuffer();
+ while (accumulated.size() < accumulateTill) {
+ using Error = AudioPlayerLoader::ReadError;
const auto result = l->readMore();
+ if (result == Error::Retry) {
+ continue;
+ }
const auto sampleBytes = v::is<bytes::const_span>(result)
? v::get<bytes::const_span>(result)
: bytes::const_span();
@@ -181,10 +193,8 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
accumulated.append(
reinterpret_cast<const char*>(sampleBytes.data()),
sampleBytes.size());
- accumulatedCount += sampleBytes.size() / l->sampleSize();
- }
- using Error = AudioPlayerLoader::ReadError;
- if (result == Error::Other) {
+ accumulatedCount += sampleBytes.size() / sampleSize;
+ } else if (result == Error::Other) {
if (errAtStart) {
{
QMutexLocker lock(internal::audioPlayerMutex());
@@ -201,7 +211,7 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
finished = true;
break;
} else if (result == Error::Wait) {
- waiting = (accumulated.size() < kPlaybackBufferSize)
+ waiting = (accumulated.size() < accumulateTill)
&& (accumulated.isEmpty() || !l->forceToBuffer());
if (waiting) {
l->saveDecodedSamples(&accumulated);
@@ -225,10 +235,11 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
return;
}
- if (started || !accumulated.isEmpty()) {
+ if (started || !accumulated.isEmpty() || updatedWithSpeed) {
Audio::AttachToDevice();
}
if (started) {
+ Assert(!updatedWithSpeed);
track->started();
if (!internal::audioCheckError()) {
setStoppedState(track, State::StoppedAtStart);
@@ -237,12 +248,20 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
}
track->format = l->format();
- track->frequency = l->samplesFrequency();
+ track->state.frequency = l->samplesFrequency();
- const auto position = (positionMs * track->frequency) / 1000LL;
- track->bufferedPosition = position;
- track->state.position = position;
- track->fadeStartPosition = position;
+ track->state.position = (positionMs * track->state.frequency)
+ / 1000LL;
+ track->updateWithSpeedPosition();
+ track->withSpeed.bufferedPosition = track->withSpeed.position;
+ track->withSpeed.fadeStartPosition = track->withSpeed.position;
+ } else if (updatedWithSpeed) {
+ auto old = Mixer::Track();
+ old.stream = base::take(track->stream);
+ old.withSpeed = std::exchange(track->withSpeed, *updatedWithSpeed);
+ track->speed = setup.newSpeed;
+ track->reattach(type);
+ old.detach();
}
if (!accumulated.isEmpty()) {
track->ensureStreamCreated(type);
@@ -256,21 +275,23 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
}
if (bufferIndex < 0) { // No free buffers, wait.
+ track->waitingForBuffer = true;
l->saveDecodedSamples(&accumulated);
return;
} else if (l->forceToBuffer()) {
l->setForceToBuffer(false);
}
+ track->waitingForBuffer = false;
- track->bufferSamples[bufferIndex] = accumulated;
- track->samplesCount[bufferIndex] = accumulatedCount;
- track->bufferedLength += accumulatedCount;
+ track->withSpeed.buffered[bufferIndex] = accumulated;
+ track->withSpeed.samples[bufferIndex] = accumulatedCount;
+ track->withSpeed.bufferedLength += accumulatedCount;
alBufferData(
track->stream.buffers[bufferIndex],
track->format,
accumulated.constData(),
accumulated.size(),
- track->frequency);
+ track->state.frequency);
alSourceQueueBuffers(
track->stream.source,
@@ -292,8 +313,11 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
if (finished) {
track->loaded = true;
- track->state.length = track->bufferedPosition + track->bufferedLength;
- clear(type);
+ track->withSpeed.length = track->withSpeed.bufferedPosition
+ + track->withSpeed.bufferedLength;
+ track->state.length = Mixer::Track::SpeedIndependentPosition(
+ track->withSpeed.length,
+ track->speed);
}
track->loading = false;
@@ -323,7 +347,10 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
}
if (state == AL_STOPPED) {
- alSourcei(track->stream.source, AL_SAMPLE_OFFSET, qMax(track->state.position - track->bufferedPosition, 0LL));
+ alSourcei(
+ track->stream.source,
+ AL_SAMPLE_OFFSET,
+ qMax(track->withSpeed.position - track->withSpeed.bufferedPosition, 0LL));
if (!internal::audioCheckError()) {
setStoppedState(track, State::StoppedAtError);
emitError(type);
@@ -340,20 +367,19 @@ void Loaders::loadData(AudioMsgId audio, crl::time positionMs) {
needToCheck();
}
-AudioPlayerLoader *Loaders::setupLoader(
+Loaders::SetupLoaderResult Loaders::setupLoader(
const AudioMsgId &audio,
- SetupError &err,
crl::time positionMs) {
- err = SetupErrorAtStart;
QMutexLocker lock(internal::audioPlayerMutex());
- if (!mixer()) return nullptr;
+ if (!mixer()) {
+ return {};
+ }
auto track = mixer()->trackForType(audio.type());
if (!track || track->state.id != audio || !track->loading) {
error(audio);
LOG(("Audio Error: trying to load part of audio, that is not current at the moment"));
- err = SetupErrorNotPlaying;
- return nullptr;
+ return {};
}
bool isGoodId = false;
@@ -369,6 +395,8 @@ AudioPlayerLoader *Loaders::setupLoader(
l = nullptr;
}
+ auto SpeedDependentPosition = Mixer::Track::SpeedDependentPosition;
+ auto SpeedIndependentPosition = Mixer::Track::SpeedIndependentPosition;
if (!l) {
std::unique_ptr<AudioPlayerLoader> *loader = nullptr;
switch (audio.type()) {
@@ -383,7 +411,7 @@ AudioPlayerLoader *Loaders::setupLoader(
track->state.state = State::StoppedAtError;
error(audio);
LOG(("Audio Error: video sound data not ready"));
- return nullptr;
+ return {};
}
*loader = std::make_unique<ChildFFMpegLoader>(
std::move(track->externalData));
@@ -395,24 +423,120 @@ AudioPlayerLoader *Loaders::setupLoader(
}
l = loader->get();
- if (!l->open(positionMs)) {
+ track->speed = track->nextSpeed;
+ if (!l->open(positionMs, track->speed)) {
track->state.state = State::StoppedAtStart;
- return nullptr;
+ return { .errorAtStart = true };
}
- auto length = l->samplesCount();
- if (length <= 0) {
+ const auto duration = l->duration();
+ if (duration <= 0) {
track->state.state = State::StoppedAtStart;
- return nullptr;
+ return { .errorAtStart = true };
}
- track->state.length = length;
track->state.frequency = l->samplesFrequency();
- err = SetupNoErrorStarted;
+ track->state.length = (duration * track->state.frequency) / 1000;
+ track->withSpeed.length = SpeedDependentPosition(
+ track->state.length,
+ track->speed);
+ return { .loader = l, .justStarted = true };
+ } else if (track->nextSpeed != track->speed) {
+ return {
+ .loader = l,
+ .oldSpeed = track->speed,
+ .newSpeed = track->nextSpeed,
+ .fadeStartPosition = track->withSpeed.fadeStartPosition,
+ .position = track->withSpeed.fineTunedPosition,
+ .normalLength = track->state.length,
+ .frequency = track->state.frequency,
+ };
} else if (track->loaded) {
- err = SetupErrorLoadedFull;
LOG(("Audio Error: trying to load part of audio, that is already loaded to the end"));
- return nullptr;
+ return {};
}
- return l;
+ return {
+ .loader = l,
+ .oldSpeed = track->speed,
+ .newSpeed = track->nextSpeed,
+ .position = track->withSpeed.fineTunedPosition,
+ .frequency = track->state.frequency,
+ };
+}
+
+Mixer::Track::WithSpeed Loaders::rebufferOnSpeedChange(
+ const SetupLoaderResult &setup) {
+ Expects(setup.oldSpeed > 0. && setup.newSpeed > 0.);
+ Expects(setup.loader != nullptr);
+
+ const auto speed = setup.newSpeed;
+ const auto change = setup.oldSpeed / speed;
+ const auto normalPosition = Mixer::Track::SpeedIndependentPosition(
+ setup.position,
+ setup.oldSpeed);
+ const auto newPosition = int64(base::SafeRound(setup.position * change));
+ auto result = Mixer::Track::WithSpeed{
+ .fineTunedPosition = newPosition,
+ .position = newPosition,
+ .length = Mixer::Track::SpeedDependentPosition(
+ setup.normalLength,
+ speed),
+ .fadeStartPosition = int64(
+ base::SafeRound(setup.fadeStartPosition * change)),
+ };
+ const auto l = setup.loader;
+ l->dropFramesTill(normalPosition);
+ const auto normalFrom = l->startReadingQueuedFrames(speed);
+ if (normalFrom < 0) {
+ result.bufferedPosition = newPosition;
+ return result;
+ }
+
+ result.bufferedPosition = Mixer::Track::SpeedDependentPosition(
+ normalFrom,
+ speed);
+ for (auto i = 0; i != Mixer::Track::kBuffersCount; ++i) {
+ auto finished = false;
+ auto accumulated = QByteArray();
+ auto accumulatedCount = int64();
+ const auto sampleSize = l->sampleSize();
+ const auto accumulateTill = l->bytesPerBuffer();
+ while (accumulated.size() < accumulateTill) {
+ const auto result = l->readMore();
+ const auto sampleBytes = v::is<bytes::const_span>(result)
+ ? v::get<bytes::const_span>(result)
+ : bytes::const_span();
+ if (!sampleBytes.empty()) {
+ accumulated.append(
+ reinterpret_cast<const char*>(sampleBytes.data()),
+ sampleBytes.size());
+ accumulatedCount += sampleBytes.size() / sampleSize;
+ continue;
+ } else if (result == AudioPlayerLoader::ReadError::Retry) {
+ continue;
+ }
+ Assert(result == AudioPlayerLoader::ReadError::RetryNotQueued);
+ finished = true;
+ break;
+ }
+ if (!accumulated.isEmpty()) {
+ result.samples[i] = accumulatedCount;
+ result.bufferedLength += accumulatedCount;
+ result.buffered[i] = accumulated;
+ }
+ if (finished) {
+ break;
+ }
+ }
+
+ const auto limit = result.bufferedPosition + result.bufferedLength;
+ if (newPosition > limit) {
+ result.fineTunedPosition = limit;
+ result.position = limit;
+ }
+ if (limit > result.length) {
+ result.length = limit;
+ }
+
+ return result;
}
Mixer::Track *Loaders::checkLoader(AudioMsgId::Type type) {