1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "media/streaming/media_streaming_audio_track.h"
9 
10 #include "media/streaming/media_streaming_utility.h"
11 #include "media/audio/media_audio.h"
12 #include "media/audio/media_child_ffmpeg_loader.h"
13 #include "media/player/media_player_instance.h"
14 
15 namespace Media {
16 namespace Streaming {
17 
AudioTrack(const PlaybackOptions & options,Stream && stream,AudioMsgId audioId,FnMut<void (const Information &)> ready,Fn<void (Error)> error)18 AudioTrack::AudioTrack(
19 	const PlaybackOptions &options,
20 	Stream &&stream,
21 	AudioMsgId audioId,
22 	FnMut<void(const Information &)> ready,
23 	Fn<void(Error)> error)
24 : _options(options)
25 , _stream(std::move(stream))
26 , _audioId(audioId)
27 , _ready(std::move(ready))
28 , _error(std::move(error))
29 , _playPosition(options.position) {
30 	Expects(_stream.duration > 1);
31 	Expects(_stream.duration != kDurationUnavailable); // Not supported.
32 	Expects(_ready != nullptr);
33 	Expects(_error != nullptr);
34 	Expects(_audioId.externalPlayId() != 0);
35 }
36 
streamIndex() const37 int AudioTrack::streamIndex() const {
38 	// Thread-safe, because _stream.index is immutable.
39 	return _stream.index;
40 }
41 
streamTimeBase() const42 AVRational AudioTrack::streamTimeBase() const {
43 	return _stream.timeBase;
44 }
45 
streamDuration() const46 crl::time AudioTrack::streamDuration() const {
47 	return _stream.duration;
48 }
49 
process(std::vector<FFmpeg::Packet> && packets)50 void AudioTrack::process(std::vector<FFmpeg::Packet> &&packets) {
51 	if (packets.empty()) {
52 		return;
53 	} else if (packets.front().empty()) {
54 		Assert(packets.size() == 1);
55 		_readTillEnd = true;
56 	}
57 	for (auto i = begin(packets), e = end(packets); i != e; ++i) {
58 		if (initialized()) {
59 			mixerEnqueue(gsl::make_span(&*i, (e - i)));
60 			break;
61 		} else if (!tryReadFirstFrame(std::move(*i))) {
62 			_error(Error::InvalidData);
63 			break;
64 		}
65 	}
66 }
67 
waitForData()68 void AudioTrack::waitForData() {
69 	if (initialized()) {
70 		mixerForceToBuffer();
71 	}
72 }
73 
initialized() const74 bool AudioTrack::initialized() const {
75 	return !_ready;
76 }
77 
tryReadFirstFrame(FFmpeg::Packet && packet)78 bool AudioTrack::tryReadFirstFrame(FFmpeg::Packet &&packet) {
79 	if (ProcessPacket(_stream, std::move(packet)).failed()) {
80 		return false;
81 	}
82 	while (true) {
83 		if (const auto error = ReadNextFrame(_stream)) {
84 			if (error.code() == AVERROR_EOF) {
85 				if (!_initialSkippingFrame) {
86 					return false;
87 				}
88 				// Return the last valid frame if we seek too far.
89 				_stream.frame = std::move(_initialSkippingFrame);
90 				return processFirstFrame();
91 			} else if (error.code() != AVERROR(EAGAIN) || _readTillEnd) {
92 				return false;
93 			} else {
94 				// Waiting for more packets.
95 				return true;
96 			}
97 		} else if (!fillStateFromFrame()) {
98 			return false;
99 		} else if (_startedPosition >= _options.position) {
100 			return processFirstFrame();
101 		}
102 
103 		// Seek was with AVSEEK_FLAG_BACKWARD so first we get old frames.
104 		// Try skipping frames until one is after the requested position.
105 		std::swap(_initialSkippingFrame, _stream.frame);
106 		if (!_stream.frame) {
107 			_stream.frame = FFmpeg::MakeFramePointer();
108 		}
109 	}
110 }
111 
processFirstFrame()112 bool AudioTrack::processFirstFrame() {
113 	if (!FFmpeg::FrameHasData(_stream.frame.get())) {
114 		return false;
115 	}
116 	mixerInit();
117 	callReady();
118 	return true;
119 }
120 
fillStateFromFrame()121 bool AudioTrack::fillStateFromFrame() {
122 	const auto position = FramePosition(_stream);
123 	if (position == kTimeUnknown) {
124 		return false;
125 	}
126 	_startedPosition = position;
127 	return true;
128 }
129 
mixerInit()130 void AudioTrack::mixerInit() {
131 	Expects(!initialized());
132 
133 	auto data = std::make_unique<ExternalSoundData>();
134 	data->frame = std::move(_stream.frame);
135 	data->codec = std::move(_stream.codec);
136 	data->frequency = _stream.frequency;
137 	data->length = (_stream.duration * data->frequency) / 1000LL;
138 	data->speed = _options.speed;
139 
140 	Media::Player::mixer()->play(
141 		_audioId,
142 		std::move(data),
143 		_startedPosition);
144 }
145 
callReady()146 void AudioTrack::callReady() {
147 	Expects(_ready != nullptr);
148 
149 	auto data = AudioInformation();
150 	data.state.duration = _stream.duration;
151 	data.state.position = _startedPosition;
152 	data.state.receivedTill = _readTillEnd
153 		? _stream.duration
154 		: _startedPosition;
155 	base::take(_ready)({ VideoInformation(), data });
156 }
157 
mixerEnqueue(gsl::span<FFmpeg::Packet> packets)158 void AudioTrack::mixerEnqueue(gsl::span<FFmpeg::Packet> packets) {
159 	Media::Player::mixer()->feedFromExternal({
160 		_audioId,
161 		packets
162 	});
163 }
164 
mixerForceToBuffer()165 void AudioTrack::mixerForceToBuffer() {
166 	Media::Player::mixer()->forceToBufferExternal(_audioId);
167 }
168 
pause(crl::time time)169 void AudioTrack::pause(crl::time time) {
170 	Expects(initialized());
171 
172 	Media::Player::mixer()->pause(_audioId, true);
173 }
174 
resume(crl::time time)175 void AudioTrack::resume(crl::time time) {
176 	Expects(initialized());
177 
178 	Media::Player::mixer()->resume(_audioId, true);
179 }
180 
stop()181 void AudioTrack::stop() {
182 	if (_audioId.externalPlayId()) {
183 		Media::Player::mixer()->stop(_audioId);
184 	}
185 }
186 
setSpeed(float64 speed)187 void AudioTrack::setSpeed(float64 speed) {
188 	_options.speed = speed;
189 	Media::Player::mixer()->setSpeedFromExternal(_audioId, speed);
190 }
191 
waitingForData() const192 rpl::producer<> AudioTrack::waitingForData() const {
193 	return _waitingForData.events();
194 }
195 
playPosition()196 rpl::producer<crl::time> AudioTrack::playPosition() {
197 	Expects(_ready == nullptr);
198 
199 	if (!_subscription) {
200 		_subscription = Media::Player::Updated(
201 		).add_subscription([=](const AudioMsgId &id) {
202 			using State = Media::Player::State;
203 			if (id != _audioId) {
204 				return;
205 			}
206 			const auto state = Media::Player::mixer()->currentState(
207 				_audioId.type());
208 			if (state.id != _audioId) {
209 				// #TODO streaming later muted by other
210 				return;
211 			} else switch (state.state) {
212 			case State::Stopped:
213 			case State::StoppedAtEnd:
214 			case State::PausedAtEnd:
215 				_playPosition.reset();
216 				return;
217 			case State::StoppedAtError:
218 			case State::StoppedAtStart:
219 				_error(Error::InvalidData);
220 				return;
221 			case State::Starting:
222 			case State::Playing:
223 			case State::Stopping:
224 			case State::Pausing:
225 			case State::Resuming:
226 				if (state.waitingForData) {
227 					_waitingForData.fire({});
228 				}
229 				_playPosition = std::clamp(
230 					crl::time((state.position * 1000 + (state.frequency / 2))
231 						/ state.frequency),
232 					crl::time(0),
233 					_stream.duration - 1);
234 				return;
235 			case State::Paused:
236 				return;
237 			}
238 		});
239 	}
240 	return _playPosition.value();
241 }
242 
~AudioTrack()243 AudioTrack::~AudioTrack() {
244 	stop();
245 }
246 
247 } // namespace Streaming
248 } // namespace Media
249