summaryrefslogtreecommitdiffstats
path: root/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.cpp
blob: 1fa8139b25b9ec07c22b09557d983ab8dcd4db55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/*
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 {
	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()) {
		return _eofReached ? ReadError::EndOfFile : ReadError::Wait;
	}

	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