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 #pragma once
9 
10 #include "data/data_audio_msg_id.h"
11 #include "data/data_shared_media.h"
12 
13 class AudioMsgId;
14 class DocumentData;
15 
16 namespace Media {
17 namespace Audio {
18 class Instance;
19 } // namespace Audio
20 } // namespace Media
21 
22 namespace Media {
23 namespace View {
24 class PlaybackProgress;
25 } // namespace View
26 } // namespace Media
27 
28 namespace Media {
29 namespace Streaming {
30 class Document;
31 class Instance;
32 struct PlaybackOptions;
33 struct Update;
34 enum class Error;
35 } // namespace Streaming
36 } // namespace Media
37 
38 namespace Media {
39 namespace Player {
40 
41 class Instance;
42 struct TrackState;
43 
44 void start(not_null<Audio::Instance*> instance);
45 void finish(not_null<Audio::Instance*> instance);
46 
47 void SaveLastPlaybackPosition(
48 	not_null<DocumentData*> document,
49 	const TrackState &state);
50 
51 not_null<Instance*> instance();
52 
53 class Instance : private base::Subscriber {
54 public:
55 	enum class Seeking {
56 		Start,
57 		Finish,
58 		Cancel,
59 	};
60 
61 	void play(AudioMsgId::Type type);
62 	void pause(AudioMsgId::Type type);
63 	void stop(AudioMsgId::Type type);
64 	void playPause(AudioMsgId::Type type);
65 	bool next(AudioMsgId::Type type);
66 	bool previous(AudioMsgId::Type type);
67 
68 	AudioMsgId::Type getActiveType() const;
69 
play()70 	void play() {
71 		play(getActiveType());
72 	}
pause()73 	void pause() {
74 		pause(getActiveType());
75 	}
stop()76 	void stop() {
77 		stop(getActiveType());
78 	}
playPause()79 	void playPause() {
80 		playPause(getActiveType());
81 	}
next()82 	bool next() {
83 		return next(getActiveType());
84 	}
previous()85 	bool previous() {
86 		return previous(getActiveType());
87 	}
88 
89 	void playPauseCancelClicked(AudioMsgId::Type type);
90 
91 	void play(const AudioMsgId &audioId);
92 	void playPause(const AudioMsgId &audioId);
93 	[[nodiscard]] TrackState getState(AudioMsgId::Type type) const;
94 
95 	[[nodiscard]] Streaming::Instance *roundVideoStreamed(
96 		HistoryItem *item) const;
97 	[[nodiscard]] View::PlaybackProgress *roundVideoPlayback(
98 		HistoryItem *item) const;
99 
current(AudioMsgId::Type type)100 	[[nodiscard]] AudioMsgId current(AudioMsgId::Type type) const {
101 		if (const auto data = getData(type)) {
102 			return data->current;
103 		}
104 		return AudioMsgId();
105 	}
106 
repeatEnabled(AudioMsgId::Type type)107 	[[nodiscard]] bool repeatEnabled(AudioMsgId::Type type) const {
108 		if (const auto data = getData(type)) {
109 			return data->repeatEnabled;
110 		}
111 		return false;
112 	}
toggleRepeat(AudioMsgId::Type type)113 	void toggleRepeat(AudioMsgId::Type type) {
114 		if (const auto data = getData(type)) {
115 			data->repeatEnabled = !data->repeatEnabled;
116 			_repeatChangedNotifier.notify(type);
117 		}
118 	}
119 
isSeeking(AudioMsgId::Type type)120 	[[nodiscard]] bool isSeeking(AudioMsgId::Type type) const {
121 		if (const auto data = getData(type)) {
122 			return (data->seeking == data->current);
123 		}
124 		return false;
125 	}
126 	void startSeeking(AudioMsgId::Type type);
127 	void finishSeeking(AudioMsgId::Type type, float64 progress);
128 	void cancelSeeking(AudioMsgId::Type type);
129 
130 	void updateVoicePlaybackSpeed();
131 
132 	[[nodiscard]] bool nextAvailable(AudioMsgId::Type type) const;
133 	[[nodiscard]] bool previousAvailable(AudioMsgId::Type type) const;
134 
135 	struct Switch {
136 		AudioMsgId from;
137 		FullMsgId to;
138 	};
139 
switchToNextNotifier()140 	base::Observable<Switch> &switchToNextNotifier() {
141 		return _switchToNextNotifier;
142 	}
playerWidgetOver()143 	base::Observable<bool> &playerWidgetOver() {
144 		return _playerWidgetOver;
145 	}
tracksFinishedNotifier()146 	base::Observable<AudioMsgId::Type> &tracksFinishedNotifier() {
147 		return _tracksFinishedNotifier;
148 	}
trackChangedNotifier()149 	base::Observable<AudioMsgId::Type> &trackChangedNotifier() {
150 		return _trackChangedNotifier;
151 	}
repeatChangedNotifier()152 	base::Observable<AudioMsgId::Type> &repeatChangedNotifier() {
153 		return _repeatChangedNotifier;
154 	}
155 
156 	rpl::producer<> playlistChanges(AudioMsgId::Type type) const;
157 
updatedNotifier()158 	rpl::producer<TrackState> updatedNotifier() const {
159 		return _updatedNotifier.events();
160 	}
161 
162 	rpl::producer<> stops(AudioMsgId::Type type) const;
163 	rpl::producer<> startsPlay(AudioMsgId::Type type) const;
164 
165 	rpl::producer<Seeking> seekingChanges(AudioMsgId::Type type) const;
166 
167 	bool pauseGifByRoundVideo() const;
168 
169 	void documentLoadProgress(DocumentData *document);
170 
171 private:
172 	using SharedMediaType = Storage::SharedMediaType;
173 	using SliceKey = SparseIdsMergedSlice::Key;
174 	struct Streamed;
175 	struct Data {
176 		Data(AudioMsgId::Type type, SharedMediaType overview);
177 		Data(Data &&other);
178 		Data &operator=(Data &&other);
179 		~Data();
180 
181 		AudioMsgId::Type type;
182 		Storage::SharedMediaType overview;
183 		AudioMsgId current;
184 		AudioMsgId seeking;
185 		std::optional<SparseIdsMergedSlice> playlistSlice;
186 		std::optional<SliceKey> playlistSliceKey;
187 		std::optional<SliceKey> playlistRequestedKey;
188 		std::optional<int> playlistIndex;
189 		rpl::lifetime playlistLifetime;
190 		rpl::lifetime sessionLifetime;
191 		rpl::event_stream<> playlistChanges;
192 		History *history = nullptr;
193 		History *migrated = nullptr;
194 		Main::Session *session = nullptr;
195 		bool repeatEnabled = false;
196 		bool isPlaying = false;
197 		bool resumeOnCallEnd = false;
198 		std::unique_ptr<Streamed> streamed;
199 	};
200 
201 	struct SeekingChanges {
202 		Seeking seeking;
203 		AudioMsgId::Type type;
204 	};
205 
206 	Instance();
207 	~Instance();
208 
209 	friend void start(not_null<Audio::Instance*> instance);
210 	friend void finish(not_null<Audio::Instance*> instance);
211 
212 	void setupShortcuts();
213 	void playStreamed(
214 		const AudioMsgId &audioId,
215 		std::shared_ptr<Streaming::Document> shared);
216 	Streaming::PlaybackOptions streamingOptions(
217 		const AudioMsgId &audioId,
218 		crl::time position = -1);
219 
220 	// Observed notifications.
221 	void handleSongUpdate(const AudioMsgId &audioId);
222 
223 	void pauseOnCall(AudioMsgId::Type type);
224 	void resumeOnCall(AudioMsgId::Type type);
225 
226 	void setCurrent(const AudioMsgId &audioId);
227 	void refreshPlaylist(not_null<Data*> data);
228 	std::optional<SliceKey> playlistKey(not_null<Data*> data) const;
229 	bool validPlaylist(not_null<Data*> data);
230 	void validatePlaylist(not_null<Data*> data);
231 	void playlistUpdated(not_null<Data*> data);
232 	bool moveInPlaylist(not_null<Data*> data, int delta, bool autonext);
233 	HistoryItem *itemByIndex(not_null<Data*> data, int index);
234 	void stopAndClear(not_null<Data*> data);
235 
236 	void handleStreamingUpdate(
237 		not_null<Data*> data,
238 		Streaming::Update &&update);
239 	void handleStreamingError(
240 		not_null<Data*> data,
241 		Streaming::Error &&error);
242 
243 	void clearStreamed(not_null<Data*> data, bool savePosition = true);
244 	void emitUpdate(AudioMsgId::Type type);
245 	template <typename CheckCallback>
246 	void emitUpdate(AudioMsgId::Type type, CheckCallback check);
247 
getData(AudioMsgId::Type type)248 	Data *getData(AudioMsgId::Type type) {
249 		if (type == AudioMsgId::Type::Song) {
250 			return &_songData;
251 		} else if (type == AudioMsgId::Type::Voice) {
252 			return &_voiceData;
253 		}
254 		return nullptr;
255 	}
256 
getData(AudioMsgId::Type type)257 	const Data *getData(AudioMsgId::Type type) const {
258 		if (type == AudioMsgId::Type::Song) {
259 			return &_songData;
260 		} else if (type == AudioMsgId::Type::Voice) {
261 			return &_voiceData;
262 		}
263 		return nullptr;
264 	}
265 
266 	HistoryItem *roundVideoItem() const;
267 	void requestRoundVideoResize() const;
268 	void requestRoundVideoRepaint() const;
269 
270 	void setHistory(not_null<Data*> data, History *history);
271 	void setSession(not_null<Data*> data, Main::Session *session);
272 
273 	Data _songData;
274 	Data _voiceData;
275 	bool _roundPlaying = false;
276 
277 	base::Observable<Switch> _switchToNextNotifier;
278 	base::Observable<bool> _playerWidgetOver;
279 	base::Observable<AudioMsgId::Type> _tracksFinishedNotifier;
280 	base::Observable<AudioMsgId::Type> _trackChangedNotifier;
281 	base::Observable<AudioMsgId::Type> _repeatChangedNotifier;
282 
283 	rpl::event_stream<AudioMsgId::Type> _playerStopped;
284 	rpl::event_stream<AudioMsgId::Type> _playerStartedPlay;
285 	rpl::event_stream<TrackState> _updatedNotifier;
286 	rpl::event_stream<SeekingChanges> _seekingChanges;
287 	rpl::lifetime _lifetime;
288 
289 };
290 
291 } // namespace Player
292 } // namespace Media
293