tdesktop/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.cpp

114 lines
2.7 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/audio/media_child_ffmpeg_loader.h"
#include "core/crash_reports.h"
#include "core/file_location.h"
namespace Media {
namespace {
using FFmpeg::AvErrorWrap;
} // namespace
ChildFFMpegLoader::ChildFFMpegLoader(
std::unique_ptr<ExternalSoundData> &&data)
: AbstractAudioFFMpegLoader(
Core::FileLocation(),
QByteArray(),
bytes::vector())
, _parentData(std::move(data)) {
Expects(_parentData->codec != nullptr);
}
bool ChildFFMpegLoader::open(crl::time positionMs, float64 speed) {
const auto sample = (positionMs * samplesFrequency()) / 1000LL;
overrideDuration(sample, _parentData->duration);
return initUsingContext(_parentData->codec.get(), speed);
}
auto ChildFFMpegLoader::readFromInitialFrame() -> ReadResult {
if (!_parentData->frame) {
return ReadError::Wait;
}
return replaceFrameAndRead(base::take(_parentData->frame));
}
auto ChildFFMpegLoader::readMore() -> ReadResult {
if (_readTillEnd) {
return ReadError::EndOfFile;
}
const auto initialFrameResult = readFromInitialFrame();
if (initialFrameResult != ReadError::Wait) {
return initialFrameResult;
}
const auto readResult = readFromReadyContext(
_parentData->codec.get());
if (readResult != ReadError::Wait) {
return readResult;
}
if (_queue.empty()) {
if (!_eofReached) {
return ReadError::Wait;
}
_readTillEnd = true;
return ReadError::EndOfFile;
}
auto packet = std::move(_queue.front());
_queue.pop_front();
_eofReached = packet.empty();
if (_eofReached) {
avcodec_send_packet(_parentData->codec.get(), nullptr); // drain
return ReadError::Retry;
}
AvErrorWrap error = avcodec_send_packet(
_parentData->codec.get(),
&packet.fields());
if (error) {
LogError(u"avcodec_send_packet"_q, error);
// There is a sample voice message where skipping such packet
// results in a crash (read_access to nullptr) in swr_convert().
if (error.code() == AVERROR_INVALIDDATA) {
return ReadError::Retry; // try to skip bad packet
}
return ReadError::Other;
}
return ReadError::Retry;
}
void ChildFFMpegLoader::enqueuePackets(
std::deque<FFmpeg::Packet> &&packets) {
if (_queue.empty()) {
_queue = std::move(packets);
} else {
_queue.insert(
end(_queue),
std::make_move_iterator(packets.begin()),
std::make_move_iterator(packets.end()));
}
packets.clear();
}
void ChildFFMpegLoader::setForceToBuffer(bool force) {
_forceToBuffer = force;
}
bool ChildFFMpegLoader::forceToBuffer() const {
return _forceToBuffer;
}
ChildFFMpegLoader::~ChildFFMpegLoader() = default;
} // namespace Media