summaryrefslogtreecommitdiffstats
path: root/Telegram/SourceFiles/media/audio/media_child_ffmpeg_loader.cpp
blob: 04c338c5d9a1dc18b3452d65993113043ff087ee (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
107
108
/*
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 {

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) {
	return initUsingContext(
		_parentData->codec.get(),
		_parentData->length,
		_parentData->frequency);
}

AudioPlayerLoader::ReadResult ChildFFMpegLoader::readFromInitialFrame() {
	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 bytes::const_span();
	}

	auto res = avcodec_send_packet(
		_parentData->codec.get(),
		&packet.fields());
	if (res < 0) {
		char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
		LOG(("Audio Error: Unable to avcodec_send_packet() file '%1', "
			"data size '%2', error %3, %4"
			).arg(_file.name()
			).arg(_data.size()
			).arg(res
			).arg(av_make_error_string(err, sizeof(err), res)));
		// There is a sample voice message where skipping such packet
		// results in a crash (read_access to nullptr) in swr_convert().
		if (res == AVERROR_INVALIDDATA) {
			return ReadError::NotYet; // try to skip bad packet
		}
		return ReadError::Other;
	}
	return bytes::const_span();
}

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