From 616d08255c728be12fa585900f993d620ec95e10 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 5 Jul 2016 20:44:02 +0300
Subject: [PATCH] Moved audio to media/media_audio and divided to several
 modules. Basic video playback with sound support in mediaview added.

---
 Telegram/SourceFiles/app.cpp                  |   2 +-
 Telegram/SourceFiles/config.h                 |   4 +-
 Telegram/SourceFiles/core/basic_types.h       |   2 +-
 Telegram/SourceFiles/history.cpp              |   2 +-
 Telegram/SourceFiles/historywidget.cpp        |   2 +-
 Telegram/SourceFiles/layout.cpp               |   2 +-
 Telegram/SourceFiles/localimageloader.cpp     |   2 +-
 Telegram/SourceFiles/mainwidget.cpp           |  16 +-
 .../{audio.cpp => media/media_audio.cpp}      | 803 ++----------------
 .../{audio.h => media/media_audio.h}          |  98 +--
 .../media/media_audio_ffmpeg_loader.cpp       | 301 +++++++
 .../media/media_audio_ffmpeg_loader.h         | 102 +++
 .../SourceFiles/media/media_audio_loader.cpp  |  80 ++
 .../SourceFiles/media/media_audio_loader.h    |  62 ++
 .../SourceFiles/media/media_audio_loaders.cpp | 423 +++++++++
 .../SourceFiles/media/media_audio_loaders.h   |  85 ++
 .../media/media_child_ffmpeg_loader.cpp       | 186 ++++
 .../media/media_child_ffmpeg_loader.h         |  93 ++
 .../SourceFiles/media/media_clip_ffmpeg.cpp   |  67 +-
 .../SourceFiles/media/media_clip_ffmpeg.h     |   4 +
 .../SourceFiles/overview/overview_layout.cpp  |   2 +-
 Telegram/SourceFiles/playerwidget.cpp         |   2 +-
 Telegram/SourceFiles/playerwidget.h           |   2 +-
 Telegram/SourceFiles/structs.cpp              |   2 +-
 Telegram/SourceFiles/structs.h                |   8 +-
 Telegram/SourceFiles/ui/twidget.h             |  10 +-
 Telegram/Telegram.pro                         |  12 +-
 Telegram/Telegram.vcxproj                     |  87 +-
 Telegram/Telegram.vcxproj.filters             |  63 +-
 Telegram/Telegram.xcodeproj/project.pbxproj   |  20 +-
 Telegram/Telegram.xcodeproj/qt_preprocess.mak |  18 +-
 31 files changed, 1684 insertions(+), 878 deletions(-)
 rename Telegram/SourceFiles/{audio.cpp => media/media_audio.cpp} (71%)
 rename Telegram/SourceFiles/{audio.h => media/media_audio.h} (79%)
 create mode 100644 Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp
 create mode 100644 Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h
 create mode 100644 Telegram/SourceFiles/media/media_audio_loader.cpp
 create mode 100644 Telegram/SourceFiles/media/media_audio_loader.h
 create mode 100644 Telegram/SourceFiles/media/media_audio_loaders.cpp
 create mode 100644 Telegram/SourceFiles/media/media_audio_loaders.h
 create mode 100644 Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp
 create mode 100644 Telegram/SourceFiles/media/media_child_ffmpeg_loader.h

diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index 9d4e55165..3c3dadca3 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "lang.h"
 #include "data/data_abstract_structure.h"
 #include "history/history_service_layout.h"
-#include "audio.h"
+#include "media/media_audio.h"
 #include "application.h"
 #include "fileuploader.h"
 #include "mainwidget.h"
diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h
index c62b700b1..6defe6349 100644
--- a/Telegram/SourceFiles/config.h
+++ b/Telegram/SourceFiles/config.h
@@ -107,12 +107,12 @@ enum {
 	AudioFadeDuration = 500,
 	AudioVoiceMsgSkip = 400, // 200ms
 	AudioVoiceMsgFade = 300, // 300ms
-	AudioPreloadSamples = 5 * 48000, // preload next part if less than 5 seconds remains
+	AudioPreloadSamples = 2 * 48000, // preload next part if less than 5 seconds remains
 	AudioVoiceMsgFrequency = 48000, // 48 kHz
 	AudioVoiceMsgMaxLength = 100 * 60, // 100 minutes
 	AudioVoiceMsgUpdateView = 100, // 100ms
 	AudioVoiceMsgChannels = 2, // stereo
-	AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers
+	AudioVoiceMsgBufferSize = 256 * 1024, // 256 Kb buffers (1.3 - 3.0 secs)
 	AudioVoiceMsgInMemory = 2 * 1024 * 1024, // 2 Mb audio is hold in memory and auto loaded
 	AudioPauseDeviceTimeout = 3000, // pause in 3 secs after playing is over
 
diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h
index c4cc892e4..6b748ae3f 100644
--- a/Telegram/SourceFiles/core/basic_types.h
+++ b/Telegram/SourceFiles/core/basic_types.h
@@ -35,7 +35,7 @@ T *getPointerAndReset(T *&ptr) {
 
 template <typename T>
 T createAndSwap(T &value) {
-	T result;
+	T result = T();
 	std::swap(result, value);
 	return result;
 }
diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index 61b78c8e8..f0f0dfea3 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -35,7 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "ui/filedialog.h"
 #include "boxes/addcontactbox.h"
 #include "boxes/confirmbox.h"
-#include "audio.h"
+#include "media/media_audio.h"
 #include "localstorage.h"
 #include "apiwrap.h"
 #include "window/top_bar_widget.h"
diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp
index c0c23cef0..8dd576800 100644
--- a/Telegram/SourceFiles/historywidget.cpp
+++ b/Telegram/SourceFiles/historywidget.cpp
@@ -40,7 +40,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "passcodewidget.h"
 #include "mainwindow.h"
 #include "fileuploader.h"
-#include "audio.h"
+#include "media/media_audio.h"
 #include "localstorage.h"
 #include "apiwrap.h"
 #include "window/top_bar_widget.h"
diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp
index 2dcd973d7..99da2c9a7 100644
--- a/Telegram/SourceFiles/layout.cpp
+++ b/Telegram/SourceFiles/layout.cpp
@@ -30,7 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "playerwidget.h"
 #include "boxes/addcontactbox.h"
 #include "boxes/confirmbox.h"
-#include "audio.h"
+#include "media/media_audio.h"
 #include "localstorage.h"
 
 TextParseOptions _textNameOptions = {
diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp
index cec36b9f4..ce407efbe 100644
--- a/Telegram/SourceFiles/localimageloader.cpp
+++ b/Telegram/SourceFiles/localimageloader.cpp
@@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "stdafx.h"
 #include "localimageloader.h"
 #include "ui/filedialog.h"
-#include "audio.h"
+#include "media/media_audio.h"
 
 #include "boxes/photosendbox.h"
 #include "media/media_clip_reader.h"
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 10688692c..558d9f092 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -47,7 +47,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "boxes/downloadpathbox.h"
 #include "localstorage.h"
 #include "shortcuts.h"
-#include "audio.h"
+#include "media/media_audio.h"
 
 StackItemSection::StackItemSection(std_::unique_ptr<Window::SectionMemento> &&memento) : StackItem(nullptr)
 , _memento(std_::move(memento)) {
@@ -1565,12 +1565,14 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) {
 		}
 	}
 
-	if (HistoryItem *item = App::histItemById(audioId.contextId())) {
-		Ui::repaintHistoryItem(item);
-	}
-	if (auto items = InlineBots::Layout::documentItems()) {
-		for (auto item : items->value(audioId.audio())) {
-			Ui::repaintInlineItem(item);
+	if (audioId.type() != AudioMsgId::Type::Video) {
+		if (auto item = App::histItemById(audioId.contextId())) {
+			Ui::repaintHistoryItem(item);
+		}
+		if (auto items = InlineBots::Layout::documentItems()) {
+			for (auto item : items->value(audioId.audio())) {
+				Ui::repaintInlineItem(item);
+			}
 		}
 	}
 }
diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp
similarity index 71%
rename from Telegram/SourceFiles/audio.cpp
rename to Telegram/SourceFiles/media/media_audio.cpp
index dfdfd17f5..52d424d40 100644
--- a/Telegram/SourceFiles/audio.cpp
+++ b/Telegram/SourceFiles/media/media_audio.cpp
@@ -19,8 +19,11 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 */
 #include "stdafx.h"
+#include "media/media_audio.h"
 
-#include "audio.h"
+#include "media/media_audio_ffmpeg_loader.h"
+#include "media/media_child_ffmpeg_loader.h"
+#include "media/media_audio_loaders.h"
 
 #include <AL/al.h>
 #include <AL/alc.h>
@@ -29,11 +32,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include <AL/alext.h>
 
 extern "C" {
-#include <libavcodec/avcodec.h>
-#include <libavformat/avformat.h>
-#include <libavutil/opt.h>
-#include <libswresample/swresample.h>
-
 #ifdef Q_OS_MAC
 #include <iconv.h>
 
@@ -274,13 +272,17 @@ void AudioPlayer::AudioMsg::clear() {
 	if (alIsSource(source)) {
 		alSourceStop(source);
 	}
-	for (int32 i = 0; i < 3; ++i) {
+	for (int i = 0; i < 3; ++i) {
 		if (samplesCount[i]) {
-			alSourceUnqueueBuffers(source, 1, buffers + i);
+			ALuint buffer = 0;
+			// This cleans some random queued buffer, not exactly the buffers[i].
+			alSourceUnqueueBuffers(source, 1, &buffer);
 			samplesCount[i] = 0;
 		}
 	}
 	nextBuffer = 0;
+
+	videoData = nullptr;
 }
 
 AudioPlayer::AudioPlayer() : _audioCurrent(0), _songCurrent(0),
@@ -311,7 +313,7 @@ _loader(new AudioPlayerLoaders(&_loaderThread)) {
 AudioPlayer::~AudioPlayer() {
 	{
 		QMutexLocker lock(&playerMutex);
-		player = 0;
+		player = nullptr;
 	}
 
 	auto clearAudioMsg = [](AudioMsg *msg) {
@@ -332,6 +334,8 @@ AudioPlayer::~AudioPlayer() {
 		clearAudioMsg(dataForType(AudioMsgId::Type::Voice, i));
 		clearAudioMsg(dataForType(AudioMsgId::Type::Song, i));
 	}
+	clearAudioMsg(&_videoData);
+
 	_faderThread.quit();
 	_loaderThread.quit();
 	_faderThread.wait();
@@ -357,6 +361,7 @@ AudioPlayer::AudioMsg *AudioPlayer::dataForType(AudioMsgId::Type type, int index
 	switch (type) {
 	case AudioMsgId::Type::Voice: return &_audioData[index];
 	case AudioMsgId::Type::Song: return &_songData[index];
+	case AudioMsgId::Type::Video: return &_videoData;
 	}
 	return nullptr;
 }
@@ -369,6 +374,7 @@ int *AudioPlayer::currentIndex(AudioMsgId::Type type) {
 	switch (type) {
 	case AudioMsgId::Type::Voice: return &_audioCurrent;
 	case AudioMsgId::Type::Song: return &_songCurrent;
+	case AudioMsgId::Type::Video: { static int videoIndex = 0; return &videoIndex; }
 	}
 	return nullptr;
 }
@@ -422,7 +428,6 @@ bool AudioPlayer::fadedStop(AudioMsgId::Type type, bool *fadedStart) {
 }
 
 void AudioPlayer::play(const AudioMsgId &audio, int64 position) {
-	bool fadedStart = false;
 	auto type = audio.type();
 	AudioMsgId stopped;
 	{
@@ -479,6 +484,40 @@ void AudioPlayer::play(const AudioMsgId &audio, int64 position) {
 	if (stopped) emit updated(stopped);
 }
 
+void AudioPlayer::playFromVideo(const AudioMsgId &audio, int64 position, std_::unique_ptr<VideoSoundData> &&data) {
+	t_assert(audio.type() == AudioMsgId::Type::Video);
+
+	auto type = audio.type();
+	AudioMsgId stopped;
+	{
+		QMutexLocker lock(&playerMutex);
+
+		auto current = dataForType(type);
+		t_assert(current != nullptr);
+
+		fadedStop(AudioMsgId::Type::Song);
+		if (current->audio) {
+			fadedStop(type);
+			stopped = current->audio;
+			emit loaderOnCancel(current->audio);
+		}
+		emit faderOnTimer();
+		current->clear();
+		current->audio = audio;
+		current->videoData = std_::move(data);
+		_loader->startFromVideo(current->videoData->videoPlayId);
+
+		current->state = AudioPlayerPlaying;
+		current->loading = true;
+		emit loaderOnStart(audio, position);
+	}
+	if (stopped) emit updated(stopped);
+}
+
+void AudioPlayer::feedFromVideo(VideoSoundPart &&part) {
+	_loader->feedFromVideo(std_::move(part));
+}
+
 bool AudioPlayer::checkCurrentALError(AudioMsgId::Type type) {
 	if (_checkALError()) return true;
 
@@ -633,6 +672,8 @@ void AudioPlayer::stopAndClear() {
 			clearAndCancel(AudioMsgId::Type::Voice, index);
 			clearAndCancel(AudioMsgId::Type::Song, index);
 		}
+		_videoData.clear();
+		_loader->stopFromVideo();
 	}
 }
 
@@ -669,6 +710,27 @@ void AudioPlayer::resumeDevice() {
 	_fader->resumeDevice();
 }
 
+
+namespace internal {
+
+QMutex *audioPlayerMutex() {
+	return &playerMutex;
+}
+
+float64 audioSuppressGain() {
+	return suppressAllGain;
+}
+
+float64 audioSuppressSongGain() {
+	return suppressSongGain;
+}
+
+bool audioCheckError() {
+	return _checkALError();
+}
+
+} // namespace internal
+
 AudioCapture::AudioCapture() : _capture(new AudioCaptureInner(&_captureThread)) {
 	connect(this, SIGNAL(captureOnStart()), _capture, SLOT(onStart()));
 	connect(this, SIGNAL(captureOnStop(bool)), _capture, SLOT(onStop(bool)));
@@ -699,7 +761,7 @@ bool AudioCapture::check() {
 }
 
 AudioCapture::~AudioCapture() {
-	capture = 0;
+	capture = nullptr;
 	_captureThread.quit();
 	_captureThread.wait();
 }
@@ -789,6 +851,7 @@ void AudioPlayerFader::onTimer() {
 		updatePlayback(AudioMsgId::Type::Voice, i, suppressAllGain, suppressAudioChanged);
 		updatePlayback(AudioMsgId::Type::Song, i, suppressGainForMusic, suppressGainForMusicChanged);
 	}
+	updatePlayback(AudioMsgId::Type::Video, 0, suppressGainForMusic, suppressGainForMusicChanged);
 
 	_songVolumeChanged = false;
 
@@ -972,710 +1035,6 @@ void AudioPlayerFader::resumeDevice() {
 	}
 }
 
-class AudioPlayerLoader {
-public:
-	AudioPlayerLoader(const FileLocation &file, const QByteArray &data) : file(file), access(false), data(data), dataPos(0) {
-	}
-	virtual ~AudioPlayerLoader() {
-		if (access) {
-			file.accessDisable();
-			access = false;
-		}
-	}
-
-	bool check(const FileLocation &file, const QByteArray &data) {
-		return this->file == file && this->data.size() == data.size();
-	}
-
-	virtual bool open(qint64 position = 0) = 0;
-	virtual int64 duration() = 0;
-	virtual int32 frequency() = 0;
-	virtual int32 format() = 0;
-	virtual int readMore(QByteArray &result, int64 &samplesAdded) = 0; // < 0 - error, 0 - nothing read, > 0 - read something
-
-protected:
-
-	FileLocation file;
-	bool access;
-	QByteArray data;
-
-	QFile f;
-	int32 dataPos;
-
-	bool openFile() {
-		if (data.isEmpty()) {
-			if (f.isOpen()) f.close();
-			if (!access) {
-				if (!file.accessEnable()) {
-					LOG(("Audio Error: could not open file access '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(f.error()).arg(f.errorString()));
-					return false;
-				}
-				access = true;
-			}
-			f.setFileName(file.name());
-			if (!f.open(QIODevice::ReadOnly)) {
-				LOG(("Audio Error: could not open file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(f.error()).arg(f.errorString()));
-				return false;
-			}
-		}
-		dataPos = 0;
-		return true;
-	}
-
-};
-
-class AbstractFFMpegLoader : public AudioPlayerLoader {
-public:
-
-	AbstractFFMpegLoader(const FileLocation &file, const QByteArray &data) : AudioPlayerLoader(file, data)
-		, freq(AudioVoiceMsgFrequency)
-		, len(0)
-		, ioBuffer(0)
-		, ioContext(0)
-		, fmtContext(0)
-		, codec(0)
-		, streamId(0)
-		, _opened(false) {
-	}
-
-	bool open(qint64 position = 0) {
-		if (!AudioPlayerLoader::openFile()) {
-			return false;
-		}
-
-		int res = 0;
-		char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
-
-		ioBuffer = (uchar*)av_malloc(AVBlockSize);
-		if (data.isEmpty()) {
-			ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast<void*>(this), &AbstractFFMpegLoader::_read_file, 0, &AbstractFFMpegLoader::_seek_file);
-		} else {
-			ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast<void*>(this), &AbstractFFMpegLoader::_read_data, 0, &AbstractFFMpegLoader::_seek_data);
-		}
-		fmtContext = avformat_alloc_context();
-		if (!fmtContext) {
-			DEBUG_LOG(("Audio Read Error: Unable to avformat_alloc_context for file '%1', data size '%2'").arg(file.name()).arg(data.size()));
-			return false;
-		}
-		fmtContext->pb = ioContext;
-
-		if ((res = avformat_open_input(&fmtContext, 0, 0, 0)) < 0) {
-			ioBuffer = 0;
-
-			DEBUG_LOG(("Audio Read Error: Unable to avformat_open_input for 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)));
-			return false;
-		}
-		_opened = true;
-
-		if ((res = avformat_find_stream_info(fmtContext, 0)) < 0) {
-			DEBUG_LOG(("Audio Read Error: Unable to avformat_find_stream_info for 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)));
-			return false;
-		}
-
-		streamId = av_find_best_stream(fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
-		if (streamId < 0) {
-			LOG(("Audio Error: Unable to av_find_best_stream for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(streamId).arg(av_make_error_string(err, sizeof(err), streamId)));
-			return false;
-		}
-
-		freq = fmtContext->streams[streamId]->codec->sample_rate;
-		if (fmtContext->streams[streamId]->duration == AV_NOPTS_VALUE) {
-			len = (fmtContext->duration * freq) / AV_TIME_BASE;
-		} else {
-			len = (fmtContext->streams[streamId]->duration * freq * fmtContext->streams[streamId]->time_base.num) / fmtContext->streams[streamId]->time_base.den;
-		}
-
-		return true;
-	}
-
-	int64 duration() {
-		return len;
-	}
-
-	int32 frequency() {
-		return freq;
-	}
-
-	~AbstractFFMpegLoader() {
-		if (ioContext) av_free(ioContext);
-		if (_opened) {
-			avformat_close_input(&fmtContext);
-		} else if (ioBuffer) {
-			av_free(ioBuffer);
-		}
-		if (fmtContext) avformat_free_context(fmtContext);
-	}
-
-protected:
-
-	int32 freq;
-	int64 len;
-
-	uchar *ioBuffer;
-	AVIOContext *ioContext;
-	AVFormatContext *fmtContext;
-	AVCodec *codec;
-	int32 streamId;
-
-	bool _opened;
-
-private:
-
-	static int _read_data(void *opaque, uint8_t *buf, int buf_size) {
-		AbstractFFMpegLoader *l = reinterpret_cast<AbstractFFMpegLoader*>(opaque);
-
-		int32 nbytes = qMin(l->data.size() - l->dataPos, int32(buf_size));
-		if (nbytes <= 0) {
-			return 0;
-		}
-
-		memcpy(buf, l->data.constData() + l->dataPos, nbytes);
-		l->dataPos += nbytes;
-		return nbytes;
-	}
-
-	static int64_t _seek_data(void *opaque, int64_t offset, int whence) {
-		AbstractFFMpegLoader *l = reinterpret_cast<AbstractFFMpegLoader*>(opaque);
-
-		int32 newPos = -1;
-		switch (whence) {
-		case SEEK_SET: newPos = offset; break;
-		case SEEK_CUR: newPos = l->dataPos + offset; break;
-		case SEEK_END: newPos = l->data.size() + offset; break;
-		}
-		if (newPos < 0 || newPos > l->data.size()) {
-			return -1;
-		}
-		l->dataPos = newPos;
-		return l->dataPos;
-	}
-
-	static int _read_file(void *opaque, uint8_t *buf, int buf_size) {
-		AbstractFFMpegLoader *l = reinterpret_cast<AbstractFFMpegLoader*>(opaque);
-		return int(l->f.read((char*)(buf), buf_size));
-	}
-
-	static int64_t _seek_file(void *opaque, int64_t offset, int whence) {
-		AbstractFFMpegLoader *l = reinterpret_cast<AbstractFFMpegLoader*>(opaque);
-
-		switch (whence) {
-		case SEEK_SET: return l->f.seek(offset) ? l->f.pos() : -1;
-		case SEEK_CUR: return l->f.seek(l->f.pos() + offset) ? l->f.pos() : -1;
-		case SEEK_END: return l->f.seek(l->f.size() + offset) ? l->f.pos() : -1;
-		}
-		return -1;
-	}
-};
-
-static const AVSampleFormat _toFormat = AV_SAMPLE_FMT_S16;
-static const int64_t _toChannelLayout = AV_CH_LAYOUT_STEREO;
-static const int32 _toChannels = 2;
-class FFMpegLoader : public AbstractFFMpegLoader {
-public:
-
-	FFMpegLoader(const FileLocation &file, const QByteArray &data) : AbstractFFMpegLoader(file, data)
-		, sampleSize(2 * sizeof(uint16))
-		, fmt(AL_FORMAT_STEREO16)
-		, srcRate(AudioVoiceMsgFrequency)
-		, dstRate(AudioVoiceMsgFrequency)
-		, maxResampleSamples(1024)
-		, dstSamplesData(0)
-		, codecContext(0)
-		, frame(0)
-		, swrContext(0) {
-		frame = av_frame_alloc();
-	}
-
-	bool open(qint64 position = 0) {
-		if (!AbstractFFMpegLoader::open(position)) {
-			return false;
-		}
-
-		int res = 0;
-		char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
-
-		// Get a pointer to the codec context for the audio stream
-		av_opt_set_int(fmtContext->streams[streamId]->codec, "refcounted_frames", 1, 0);
-		if ((res = avcodec_open2(fmtContext->streams[streamId]->codec, codec, 0)) < 0) {
-			LOG(("Audio Error: Unable to avcodec_open2 for 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)));
-			return false;
-		}
-		codecContext = fmtContext->streams[streamId]->codec;
-
-		uint64_t layout = codecContext->channel_layout;
-		inputFormat = codecContext->sample_fmt;
-		switch (layout) {
-		case AV_CH_LAYOUT_MONO:
-			switch (inputFormat) {
-			case AV_SAMPLE_FMT_U8:
-			case AV_SAMPLE_FMT_U8P: fmt = AL_FORMAT_MONO8; sampleSize = 1; break;
-			case AV_SAMPLE_FMT_S16:
-			case AV_SAMPLE_FMT_S16P: fmt = AL_FORMAT_MONO16; sampleSize = sizeof(uint16); break;
-			default:
-				sampleSize = -1; // convert needed
-				break;
-			}
-			break;
-		case AV_CH_LAYOUT_STEREO:
-			switch (inputFormat) {
-			case AV_SAMPLE_FMT_U8: fmt = AL_FORMAT_STEREO8; sampleSize = 2; break;
-			case AV_SAMPLE_FMT_S16: fmt = AL_FORMAT_STEREO16; sampleSize = 2 * sizeof(uint16); break;
-			default:
-				sampleSize = -1; // convert needed
-				break;
-			}
-			break;
-		default:
-			sampleSize = -1; // convert needed
-			break;
-		}
-		if (freq != 44100 && freq != 48000) {
-			sampleSize = -1; // convert needed
-		}
-
-		if (sampleSize < 0) {
-			swrContext = swr_alloc();
-			if (!swrContext) {
-				LOG(("Audio Error: Unable to swr_alloc for file '%1', data size '%2'").arg(file.name()).arg(data.size()));
-				return false;
-			}
-			int64_t src_ch_layout = layout, dst_ch_layout = _toChannelLayout;
-			srcRate = freq;
-			AVSampleFormat src_sample_fmt = inputFormat, dst_sample_fmt = _toFormat;
-			dstRate = (freq != 44100 && freq != 48000) ? AudioVoiceMsgFrequency : freq;
-
-			av_opt_set_int(swrContext, "in_channel_layout", src_ch_layout, 0);
-			av_opt_set_int(swrContext, "in_sample_rate", srcRate, 0);
-			av_opt_set_sample_fmt(swrContext, "in_sample_fmt", src_sample_fmt, 0);
-			av_opt_set_int(swrContext, "out_channel_layout", dst_ch_layout, 0);
-			av_opt_set_int(swrContext, "out_sample_rate", dstRate, 0);
-			av_opt_set_sample_fmt(swrContext, "out_sample_fmt", dst_sample_fmt, 0);
-
-			if ((res = swr_init(swrContext)) < 0) {
-				LOG(("Audio Error: Unable to swr_init for 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)));
-				return false;
-			}
-
-			sampleSize = _toChannels * sizeof(short);
-			freq = dstRate;
-			len = av_rescale_rnd(len, dstRate, srcRate, AV_ROUND_UP);
-			fmt = AL_FORMAT_STEREO16;
-
-			maxResampleSamples = av_rescale_rnd(AVBlockSize / sampleSize, dstRate, srcRate, AV_ROUND_UP);
-			if ((res = av_samples_alloc_array_and_samples(&dstSamplesData, 0, _toChannels, maxResampleSamples, _toFormat, 0)) < 0) {
-				LOG(("Audio Error: Unable to av_samples_alloc for 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)));
-				return false;
-			}
-		}
-		if (position) {
-			int64 ts = (position * fmtContext->streams[streamId]->time_base.den) / (freq * fmtContext->streams[streamId]->time_base.num);
-			if (av_seek_frame(fmtContext, streamId, ts, AVSEEK_FLAG_ANY) < 0) {
-				if (av_seek_frame(fmtContext, streamId, ts, 0) < 0) {
-				}
-			}
-			//if (dstSamplesData) {
-			//	position = qRound(srcRate * (position / float64(dstRate)));
-			//}
-		}
-
-		return true;
-	}
-
-	int32 format() {
-		return fmt;
-	}
-
-	int readMore(QByteArray &result, int64 &samplesAdded) {
-		int res;
-		if ((res = av_read_frame(fmtContext, &avpkt)) < 0) {
-			if (res != AVERROR_EOF) {
-				char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
-				LOG(("Audio Error: Unable to av_read_frame() 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)));
-			}
-			return -1;
-		}
-		if (avpkt.stream_index == streamId) {
-			av_frame_unref(frame);
-			int got_frame = 0;
-			if ((res = avcodec_decode_audio4(codecContext, frame, &got_frame, &avpkt)) < 0) {
-				char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
-				LOG(("Audio Error: Unable to avcodec_decode_audio4() 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)));
-
-				av_packet_unref(&avpkt);
-				if (res == AVERROR_INVALIDDATA) return 0; // try to skip bad packet
-				return -1;
-			}
-
-			if (got_frame) {
-				if (dstSamplesData) { // convert needed
-					int64_t dstSamples = av_rescale_rnd(swr_get_delay(swrContext, srcRate) + frame->nb_samples, dstRate, srcRate, AV_ROUND_UP);
-					if (dstSamples > maxResampleSamples) {
-						maxResampleSamples = dstSamples;
-						av_free(dstSamplesData[0]);
-
-						if ((res = av_samples_alloc(dstSamplesData, 0, _toChannels, maxResampleSamples, _toFormat, 1)) < 0) {
-							dstSamplesData[0] = 0;
-							char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
-							LOG(("Audio Error: Unable to av_samples_alloc for 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)));
-
-							av_packet_unref(&avpkt);
-							return -1;
-						}
-					}
-					if ((res = swr_convert(swrContext, dstSamplesData, dstSamples, (const uint8_t**)frame->extended_data, frame->nb_samples)) < 0) {
-						char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
-						LOG(("Audio Error: Unable to swr_convert for 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)));
-
-						av_packet_unref(&avpkt);
-						return -1;
-					}
-					int32 resultLen = av_samples_get_buffer_size(0, _toChannels, res, _toFormat, 1);
-					result.append((const char*)dstSamplesData[0], resultLen);
-					samplesAdded += resultLen / sampleSize;
-				} else {
-					result.append((const char*)frame->extended_data[0], frame->nb_samples * sampleSize);
-					samplesAdded += frame->nb_samples;
-				}
-			}
-		}
-		av_packet_unref(&avpkt);
-		return 1;
-	}
-
-	~FFMpegLoader() {
-		if (codecContext) avcodec_close(codecContext);
-		if (swrContext) swr_free(&swrContext);
-		if (dstSamplesData) {
-			if (dstSamplesData[0]) {
-				av_freep(&dstSamplesData[0]);
-			}
-			av_freep(&dstSamplesData);
-		}
-		av_frame_free(&frame);
-	}
-
-protected:
-	int32 sampleSize;
-
-private:
-
-	int32 fmt;
-	int32 srcRate, dstRate, maxResampleSamples;
-	uint8_t **dstSamplesData;
-
-	AVCodecContext *codecContext;
-	AVPacket avpkt;
-	AVSampleFormat inputFormat;
-	AVFrame *frame;
-
-	SwrContext *swrContext;
-
-};
-
-AudioPlayerLoaders::AudioPlayerLoaders(QThread *thread) : _audioLoader(0), _songLoader(0) {
-	moveToThread(thread);
-}
-
-AudioPlayerLoaders::~AudioPlayerLoaders() {
-	delete _audioLoader;
-	delete _songLoader;
-}
-
-void AudioPlayerLoaders::onInit() {
-}
-
-void AudioPlayerLoaders::onStart(const AudioMsgId &audio, qint64 position) {
-	auto type = audio.type();
-	clear(type);
-	{
-		QMutexLocker lock(&playerMutex);
-		AudioPlayer *voice = audioPlayer();
-		if (!voice) return;
-
-		auto data = voice->dataForType(type);
-		if (!data) return;
-
-		data->loading = true;
-	}
-
-	loadData(audio, position);
-}
-
-void AudioPlayerLoaders::clear(AudioMsgId::Type type) {
-	switch (type) {
-	case AudioMsgId::Type::Voice: clearAudio(); break;
-	case AudioMsgId::Type::Song: clearSong(); break;
-	}
-}
-
-void AudioPlayerLoaders::setStoppedState(AudioPlayer::AudioMsg *m, AudioPlayerState state) {
-	m->state = state;
-	m->position = 0;
-}
-
-void AudioPlayerLoaders::emitError(AudioMsgId::Type type) {
-	switch (type) {
-	case AudioMsgId::Type::Voice: emit error(clearAudio()); break;
-	case AudioMsgId::Type::Song: emit error(clearSong()); break;
-	}
-}
-
-AudioMsgId AudioPlayerLoaders::clearAudio() {
-	AudioMsgId current = _audio;
-	_audio = AudioMsgId();
-	delete _audioLoader;
-	_audioLoader = nullptr;
-	return current;
-}
-
-AudioMsgId AudioPlayerLoaders::clearSong() {
-	AudioMsgId current = _song;
-	_song = AudioMsgId();
-	delete _songLoader;
-	_songLoader = nullptr;
-	return current;
-}
-
-void AudioPlayerLoaders::onLoad(const AudioMsgId &audio) {
-	loadData(audio, 0);
-}
-
-void AudioPlayerLoaders::loadData(const AudioMsgId &audio, qint64 position) {
-	SetupError err = SetupNoErrorStarted;
-	auto type = audio.type();
-	AudioPlayerLoader *l = setupLoader(audio, err, position);
-	if (!l) {
-		if (err == SetupErrorAtStart) {
-			emitError(type);
-		}
-		return;
-	}
-
-	bool started = (err == SetupNoErrorStarted), finished = false, errAtStart = started;
-
-	QByteArray result;
-	int64 samplesAdded = 0, frequency = l->frequency(), format = l->format();
-	while (result.size() < AudioVoiceMsgBufferSize) {
-		int res = l->readMore(result, samplesAdded);
-		if (res < 0) {
-			if (errAtStart) {
-				{
-					QMutexLocker lock(&playerMutex);
-					AudioPlayer::AudioMsg *m = checkLoader(type);
-					if (m) m->state = AudioPlayerStoppedAtStart;
-				}
-				emitError(type);
-				return;
-			}
-			finished = true;
-			break;
-		}
-		if (res > 0) errAtStart = false;
-
-		QMutexLocker lock(&playerMutex);
-		if (!checkLoader(type)) {
-			clear(type);
-			return;
-		}
-	}
-
-	QMutexLocker lock(&playerMutex);
-	AudioPlayer::AudioMsg *m = checkLoader(type);
-	if (!m) {
-		clear(type);
-		return;
-	}
-
-	if (started) {
-		if (m->source) {
-			alSourceStop(m->source);
-			for (int32 i = 0; i < 3; ++i) {
-				if (m->samplesCount[i]) {
-					alSourceUnqueueBuffers(m->source, 1, m->buffers + i);
-					m->samplesCount[i] = 0;
-				}
-			}
-			m->nextBuffer = 0;
-		}
-		m->skipStart = position;
-		m->skipEnd = m->duration - position;
-		m->position = 0;
-		m->started = 0;
-	}
-	if (samplesAdded) {
-		if (!m->source) {
-			alGenSources(1, &m->source);
-			alSourcef(m->source, AL_PITCH, 1.f);
-			alSource3f(m->source, AL_POSITION, 0, 0, 0);
-			alSource3f(m->source, AL_VELOCITY, 0, 0, 0);
-			alSourcei(m->source, AL_LOOPING, 0);
-		}
-		if (!m->buffers[m->nextBuffer]) alGenBuffers(3, m->buffers);
-		if (!_checkALError()) {
-			setStoppedState(m, AudioPlayerStoppedAtError);
-			emitError(type);
-			return;
-		}
-
-		if (m->samplesCount[m->nextBuffer]) {
-			alSourceUnqueueBuffers(m->source, 1, m->buffers + m->nextBuffer);
-			m->skipStart += m->samplesCount[m->nextBuffer];
-		}
-
-		m->samplesCount[m->nextBuffer] = samplesAdded;
-		alBufferData(m->buffers[m->nextBuffer], format, result.constData(), result.size(), frequency);
-		alSourceQueueBuffers(m->source, 1, m->buffers + m->nextBuffer);
-		m->skipEnd -= samplesAdded;
-
-		m->nextBuffer = (m->nextBuffer + 1) % 3;
-
-		if (!_checkALError()) {
-			setStoppedState(m, AudioPlayerStoppedAtError);
-			emitError(type);
-			return;
-		}
-	} else {
-		finished = true;
-	}
-	if (finished) {
-		m->skipEnd = 0;
-		m->duration = m->skipStart + m->samplesCount[0] + m->samplesCount[1] + m->samplesCount[2];
-		clear(type);
-	}
-	m->loading = false;
-	if (m->state == AudioPlayerResuming || m->state == AudioPlayerPlaying || m->state == AudioPlayerStarting) {
-		ALint state = AL_INITIAL;
-		alGetSourcei(m->source, AL_SOURCE_STATE, &state);
-		if (_checkALError()) {
-			if (state != AL_PLAYING) {
-				audioPlayer()->resumeDevice();
-
-				switch (type) {
-				case AudioMsgId::Type::Voice: alSourcef(m->source, AL_GAIN, suppressAllGain); break;
-				case AudioMsgId::Type::Song: alSourcef(m->source, AL_GAIN, suppressSongGain * cSongVolume()); break;
-				}
-				if (!_checkALError()) {
-					setStoppedState(m, AudioPlayerStoppedAtError);
-					emitError(type);
-					return;
-				}
-
-				alSourcePlay(m->source);
-				if (!_checkALError()) {
-					setStoppedState(m, AudioPlayerStoppedAtError);
-					emitError(type);
-					return;
-				}
-
-				emit needToCheck();
-			}
-		} else {
-			setStoppedState(m, AudioPlayerStoppedAtError);
-			emitError(type);
-		}
-	}
-}
-
-AudioPlayerLoader *AudioPlayerLoaders::setupLoader(const AudioMsgId &audio, SetupError &err, qint64 position) {
-	err = SetupErrorAtStart;
-	QMutexLocker lock(&playerMutex);
-	AudioPlayer *voice = audioPlayer();
-	if (!voice) return nullptr;
-
-	auto data = voice->dataForType(audio.type());
-	if (!data || data->audio != audio || !data->loading) {
-		emit error(audio);
-		LOG(("Audio Error: trying to load part of audio, that is not current at the moment"));
-		err = SetupErrorNotPlaying;
-		return nullptr;
-	}
-
-	bool isGoodId = false;
-	AudioPlayerLoader **l = nullptr;
-	switch (audio.type()) {
-	case AudioMsgId::Type::Voice: l = &_audioLoader; isGoodId = (_audio == audio); break;
-	case AudioMsgId::Type::Song: l = &_songLoader; isGoodId = (_song == audio); break;
-	}
-
-	if (*l && (!isGoodId || !(*l)->check(data->file, data->data))) {
-		delete *l;
-		*l = nullptr;
-		switch (audio.type()) {
-		case AudioMsgId::Type::Voice: _audio = AudioMsgId(); break;
-		case AudioMsgId::Type::Song: _song = AudioMsgId(); break;
-		}
-	}
-
-	if (!*l) {
-		switch (audio.type()) {
-		case AudioMsgId::Type::Voice: _audio = audio; break;
-		case AudioMsgId::Type::Song: _song = audio; break;
-		}
-
-		*l = new FFMpegLoader(data->file, data->data);
-
-		if (!(*l)->open(position)) {
-			data->state = AudioPlayerStoppedAtStart;
-			return nullptr;
-		}
-		int64 duration = (*l)->duration();
-		if (duration <= 0) {
-			data->state = AudioPlayerStoppedAtStart;
-			return nullptr;
-		}
-		data->duration = duration;
-		data->frequency = (*l)->frequency();
-		if (!data->frequency) data->frequency = AudioVoiceMsgFrequency;
-		err = SetupNoErrorStarted;
-	} else {
-		if (!data->skipEnd) {
-			err = SetupErrorLoadedFull;
-			LOG(("Audio Error: trying to load part of audio, that is already loaded to the end"));
-			return nullptr;
-		}
-	}
-	return *l;
-}
-
-AudioPlayer::AudioMsg *AudioPlayerLoaders::checkLoader(AudioMsgId::Type type) {
-	AudioPlayer *voice = audioPlayer();
-	if (!voice) return 0;
-
-	auto data = voice->dataForType(type);
-	bool isGoodId = false;
-	AudioPlayerLoader **l = nullptr;
-	switch (type) {
-	case AudioMsgId::Type::Voice: l = &_audioLoader; isGoodId = (data->audio == _audio); break;
-	case AudioMsgId::Type::Song: l = &_songLoader; isGoodId = (data->audio == _song); break;
-	}
-	if (!l || !data) return nullptr;
-
-	if (!isGoodId || !data->loading || !(*l)->check(data->file, data->data)) {
-		LOG(("Audio Error: playing changed while loading"));
-		return nullptr;
-	}
-
-	return data;
-}
-
-void AudioPlayerLoaders::onCancel(const AudioMsgId &audio) {
-	switch (audio.type()) {
-	case AudioMsgId::Type::Voice: if (_audio == audio) clear(audio.type()); break;
-	case AudioMsgId::Type::Song: if (_song == audio) clear(audio.type()); break;
-	}
-
-	QMutexLocker lock(&playerMutex);
-	AudioPlayer *voice = audioPlayer();
-	if (!voice) return;
-
-	for (int i = 0; i < AudioSimultaneousLimit; ++i) {
-		auto data = voice->dataForType(audio.type(), i);
-		if (data->audio == audio) {
-			data->loading = false;
-		}
-	}
-}
-
 struct AudioCapturePrivate {
 	AudioCapturePrivate()
 		: device(0)
@@ -2232,7 +1591,7 @@ public:
 	FFMpegAttributesReader(const FileLocation &file, const QByteArray &data) : AbstractFFMpegLoader(file, data) {
 	}
 
-	bool open(qint64 position = 0) {
+	bool open(qint64 position = 0) override {
 		if (!AbstractFFMpegLoader::open()) {
 			return false;
 		}
@@ -2287,7 +1646,7 @@ public:
 		//}
 	}
 
-	int32 format() {
+	int32 format() override {
 		return 0;
 	}
 
@@ -2311,9 +1670,9 @@ public:
 		return _coverFormat;
 	}
 
-	int readMore(QByteArray &result, int64 &samplesAdded) {
+	ReadResult readMore(QByteArray &result, int64 &samplesAdded) override {
 		DEBUG_LOG(("Audio Read Error: should not call this"));
-		return -1;
+		return ReadResult::Error;
 	}
 
 	~FFMpegAttributesReader() {
@@ -2347,7 +1706,7 @@ public:
 	FFMpegWaveformCounter(const FileLocation &file, const QByteArray &data) : FFMpegLoader(file, data) {
 	}
 
-	bool open(qint64 position = 0) {
+	bool open(qint64 position = 0) override {
 		if (!FFMpegLoader::open(position)) {
 			return false;
 		}
@@ -2368,8 +1727,8 @@ public:
 			buffer.resize(0);
 
 			int64 samples = 0;
-			int res = readMore(buffer, samples);
-			if (res < 0) {
+			auto res = readMore(buffer, samples);
+			if (res == ReadResult::Error) {
 				break;
 			}
 			if (buffer.isEmpty()) {
diff --git a/Telegram/SourceFiles/audio.h b/Telegram/SourceFiles/media/media_audio.h
similarity index 79%
rename from Telegram/SourceFiles/audio.h
rename to Telegram/SourceFiles/media/media_audio.h
index e0ff14e5a..27a8e15a6 100644
--- a/Telegram/SourceFiles/audio.h
+++ b/Telegram/SourceFiles/media/media_audio.h
@@ -28,24 +28,27 @@ void audioPlayNotify();
 void audioFinish();
 
 enum AudioPlayerState {
-	AudioPlayerStopped        = 0x01,
-	AudioPlayerStoppedAtEnd   = 0x02,
+	AudioPlayerStopped = 0x01,
+	AudioPlayerStoppedAtEnd = 0x02,
 	AudioPlayerStoppedAtError = 0x03,
 	AudioPlayerStoppedAtStart = 0x04,
-	AudioPlayerStoppedMask    = 0x07,
+	AudioPlayerStoppedMask = 0x07,
 
-	AudioPlayerStarting       = 0x08,
-	AudioPlayerPlaying        = 0x10,
-	AudioPlayerFinishing      = 0x18,
-	AudioPlayerPausing        = 0x20,
-	AudioPlayerPaused         = 0x28,
-	AudioPlayerPausedAtEnd    = 0x30,
-	AudioPlayerResuming       = 0x38,
+	AudioPlayerStarting = 0x08,
+	AudioPlayerPlaying = 0x10,
+	AudioPlayerFinishing = 0x18,
+	AudioPlayerPausing = 0x20,
+	AudioPlayerPaused = 0x28,
+	AudioPlayerPausedAtEnd = 0x30,
+	AudioPlayerResuming = 0x38,
 };
 
 class AudioPlayerFader;
 class AudioPlayerLoaders;
 
+struct VideoSoundData;
+struct VideoSoundPart;
+
 class AudioPlayer : public QObject {
 	Q_OBJECT
 
@@ -58,6 +61,10 @@ public:
 	void seek(int64 position); // type == AudioMsgId::Type::Song
 	void stop(AudioMsgId::Type type);
 
+	// Video player audio stream interface.
+	void playFromVideo(const AudioMsgId &audio, int64 position, std_::unique_ptr<VideoSoundData> &&data);
+	void feedFromVideo(VideoSoundPart &&part);
+
 	void stopAndClear();
 
 	void currentState(AudioMsgId *audio, AudioMsgId::Type type, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0);
@@ -115,6 +122,8 @@ private:
 		int32 nextBuffer = 0;
 		uint32 buffers[3] = { 0 };
 		int64 samplesCount[3] = { 0 };
+
+		std_::unique_ptr<VideoSoundData> videoData;
 	};
 
 	void currentState(AudioMsg *current, AudioPlayerState *state, int64 *position, int64 *duration, int32 *frequency);
@@ -131,6 +140,8 @@ private:
 	int _songCurrent;
 	AudioMsg _songData[AudioSimultaneousLimit];
 
+	AudioMsg _videoData;
+
 	QMutex _mutex;
 
 	friend class AudioPlayerFader;
@@ -142,6 +153,15 @@ private:
 
 };
 
+namespace internal {
+
+QMutex *audioPlayerMutex();
+float64 audioSuppressGain();
+float64 audioSuppressSongGain();
+bool audioCheckError();
+
+} // namespace internal
+
 class AudioCaptureInner;
 
 class AudioCapture : public QObject {
@@ -196,7 +216,7 @@ signals:
 
 	void stopPauseDevice();
 
-public slots:
+	public slots:
 
 	void onInit();
 	void onTimer();
@@ -211,10 +231,10 @@ public slots:
 private:
 
 	enum {
-		EmitError           = 0x01,
-		EmitStopped         = 0x02,
+		EmitError = 0x01,
+		EmitStopped = 0x02,
 		EmitPositionUpdated = 0x04,
-		EmitNeedToPreload   = 0x08,
+		EmitNeedToPreload = 0x08,
 	};
 	int32 updateOnePlayback(AudioPlayer::AudioMsg *m, bool &hasPlaying, bool &hasFading, float64 suppressGain, bool suppressGainChanged);
 	void setStoppedState(AudioPlayer::AudioMsg *m, AudioPlayerState state = AudioPlayerStopped);
@@ -229,54 +249,6 @@ private:
 
 };
 
-class AudioPlayerLoader;
-class AudioPlayerLoaders : public QObject {
-	Q_OBJECT
-
-public:
-
-	AudioPlayerLoaders(QThread *thread);
-	~AudioPlayerLoaders();
-
-signals:
-
-	void error(const AudioMsgId &audio);
-	void needToCheck();
-
-public slots:
-
-	void onInit();
-
-	void onStart(const AudioMsgId &audio, qint64 position);
-	void onLoad(const AudioMsgId &audio);
-	void onCancel(const AudioMsgId &audio);
-
-private:
-
-	AudioMsgId _audio;
-	AudioPlayerLoader *_audioLoader;
-
-	AudioMsgId _song;
-	AudioPlayerLoader *_songLoader;
-
-	void emitError(AudioMsgId::Type type);
-	void clear(AudioMsgId::Type type);
-	void setStoppedState(AudioPlayer::AudioMsg *m, AudioPlayerState state = AudioPlayerStopped);
-	AudioMsgId clearAudio();
-	AudioMsgId clearSong();
-
-	enum SetupError {
-		SetupErrorAtStart    = 0,
-		SetupErrorNotPlaying = 1,
-		SetupErrorLoadedFull = 2,
-		SetupNoErrorStarted  = 3,
-	};
-	void loadData(const AudioMsgId &audio, qint64 position);
-	AudioPlayerLoader *setupLoader(const AudioMsgId &audio, SetupError &err, qint64 position);
-	AudioPlayer::AudioMsg *checkLoader(AudioMsgId::Type type);
-
-};
-
 struct AudioCapturePrivate;
 
 class AudioCaptureInner : public QObject {
@@ -293,7 +265,7 @@ signals:
 	void update(quint16 level, qint32 samples);
 	void done(QByteArray data, VoiceWaveform waveform, qint32 samples);
 
-public slots:
+	public slots:
 
 	void onInit();
 	void onStart();
diff --git a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp
new file mode 100644
index 000000000..ab7a14156
--- /dev/null
+++ b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp
@@ -0,0 +1,301 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+In addition, as a special exception, the copyright holders give permission
+to link the code of portions of this program with the OpenSSL library.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
+*/
+#include "stdafx.h"
+#include "media/media_audio_ffmpeg_loader.h"
+
+constexpr AVSampleFormat AudioToFormat = AV_SAMPLE_FMT_S16;
+constexpr int64_t AudioToChannelLayout = AV_CH_LAYOUT_STEREO;
+constexpr int32 AudioToChannels = 2;
+
+bool AbstractFFMpegLoader::open(qint64 position) {
+	if (!AudioPlayerLoader::openFile()) {
+		return false;
+	}
+
+	int res = 0;
+	char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+
+	ioBuffer = (uchar*)av_malloc(AVBlockSize);
+	if (data.isEmpty()) {
+		ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast<void*>(this), &AbstractFFMpegLoader::_read_file, 0, &AbstractFFMpegLoader::_seek_file);
+	} else {
+		ioContext = avio_alloc_context(ioBuffer, AVBlockSize, 0, reinterpret_cast<void*>(this), &AbstractFFMpegLoader::_read_data, 0, &AbstractFFMpegLoader::_seek_data);
+	}
+	fmtContext = avformat_alloc_context();
+	if (!fmtContext) {
+		DEBUG_LOG(("Audio Read Error: Unable to avformat_alloc_context for file '%1', data size '%2'").arg(file.name()).arg(data.size()));
+		return false;
+	}
+	fmtContext->pb = ioContext;
+
+	if ((res = avformat_open_input(&fmtContext, 0, 0, 0)) < 0) {
+		ioBuffer = 0;
+
+		DEBUG_LOG(("Audio Read Error: Unable to avformat_open_input for 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)));
+		return false;
+	}
+	_opened = true;
+
+	if ((res = avformat_find_stream_info(fmtContext, 0)) < 0) {
+		DEBUG_LOG(("Audio Read Error: Unable to avformat_find_stream_info for 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)));
+		return false;
+	}
+
+	streamId = av_find_best_stream(fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
+	if (streamId < 0) {
+		LOG(("Audio Error: Unable to av_find_best_stream for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(streamId).arg(av_make_error_string(err, sizeof(err), streamId)));
+		return false;
+	}
+
+	freq = fmtContext->streams[streamId]->codec->sample_rate;
+	if (fmtContext->streams[streamId]->duration == AV_NOPTS_VALUE) {
+		len = (fmtContext->duration * freq) / AV_TIME_BASE;
+	} else {
+		len = (fmtContext->streams[streamId]->duration * freq * fmtContext->streams[streamId]->time_base.num) / fmtContext->streams[streamId]->time_base.den;
+	}
+
+	return true;
+}
+
+AbstractFFMpegLoader::~AbstractFFMpegLoader() {
+	if (ioContext) av_free(ioContext);
+	if (_opened) {
+		avformat_close_input(&fmtContext);
+	} else if (ioBuffer) {
+		av_free(ioBuffer);
+	}
+	if (fmtContext) avformat_free_context(fmtContext);
+}
+
+int AbstractFFMpegLoader::_read_data(void *opaque, uint8_t *buf, int buf_size) {
+	AbstractFFMpegLoader *l = reinterpret_cast<AbstractFFMpegLoader*>(opaque);
+
+	int32 nbytes = qMin(l->data.size() - l->dataPos, int32(buf_size));
+	if (nbytes <= 0) {
+		return 0;
+	}
+
+	memcpy(buf, l->data.constData() + l->dataPos, nbytes);
+	l->dataPos += nbytes;
+	return nbytes;
+}
+
+int64_t AbstractFFMpegLoader::_seek_data(void *opaque, int64_t offset, int whence) {
+	AbstractFFMpegLoader *l = reinterpret_cast<AbstractFFMpegLoader*>(opaque);
+
+	int32 newPos = -1;
+	switch (whence) {
+	case SEEK_SET: newPos = offset; break;
+	case SEEK_CUR: newPos = l->dataPos + offset; break;
+	case SEEK_END: newPos = l->data.size() + offset; break;
+	}
+	if (newPos < 0 || newPos > l->data.size()) {
+		return -1;
+	}
+	l->dataPos = newPos;
+	return l->dataPos;
+}
+
+int AbstractFFMpegLoader::_read_file(void *opaque, uint8_t *buf, int buf_size) {
+	AbstractFFMpegLoader *l = reinterpret_cast<AbstractFFMpegLoader*>(opaque);
+	return int(l->f.read((char*)(buf), buf_size));
+}
+
+int64_t AbstractFFMpegLoader::_seek_file(void *opaque, int64_t offset, int whence) {
+	AbstractFFMpegLoader *l = reinterpret_cast<AbstractFFMpegLoader*>(opaque);
+
+	switch (whence) {
+	case SEEK_SET: return l->f.seek(offset) ? l->f.pos() : -1;
+	case SEEK_CUR: return l->f.seek(l->f.pos() + offset) ? l->f.pos() : -1;
+	case SEEK_END: return l->f.seek(l->f.size() + offset) ? l->f.pos() : -1;
+	}
+	return -1;
+}
+
+FFMpegLoader::FFMpegLoader(const FileLocation &file, const QByteArray &data) : AbstractFFMpegLoader(file, data) {
+	frame = av_frame_alloc();
+}
+
+bool FFMpegLoader::open(qint64 position) {
+	if (!AbstractFFMpegLoader::open(position)) {
+		return false;
+	}
+
+	int res = 0;
+	char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+
+	// Get a pointer to the codec context for the audio stream
+	av_opt_set_int(fmtContext->streams[streamId]->codec, "refcounted_frames", 1, 0);
+	if ((res = avcodec_open2(fmtContext->streams[streamId]->codec, codec, 0)) < 0) {
+		LOG(("Audio Error: Unable to avcodec_open2 for 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)));
+		return false;
+	}
+	codecContext = fmtContext->streams[streamId]->codec;
+
+	uint64_t layout = codecContext->channel_layout;
+	inputFormat = codecContext->sample_fmt;
+	switch (layout) {
+	case AV_CH_LAYOUT_MONO:
+		switch (inputFormat) {
+		case AV_SAMPLE_FMT_U8:
+		case AV_SAMPLE_FMT_U8P: fmt = AL_FORMAT_MONO8; sampleSize = 1; break;
+		case AV_SAMPLE_FMT_S16:
+		case AV_SAMPLE_FMT_S16P: fmt = AL_FORMAT_MONO16; sampleSize = sizeof(uint16); break;
+		default:
+			sampleSize = -1; // convert needed
+		break;
+		}
+	break;
+	case AV_CH_LAYOUT_STEREO:
+		switch (inputFormat) {
+		case AV_SAMPLE_FMT_U8: fmt = AL_FORMAT_STEREO8; sampleSize = 2; break;
+		case AV_SAMPLE_FMT_S16: fmt = AL_FORMAT_STEREO16; sampleSize = 2 * sizeof(uint16); break;
+		default:
+			sampleSize = -1; // convert needed
+		break;
+		}
+	break;
+	default:
+		sampleSize = -1; // convert needed
+	break;
+	}
+	if (freq != 44100 && freq != 48000) {
+		sampleSize = -1; // convert needed
+	}
+
+	if (sampleSize < 0) {
+		swrContext = swr_alloc();
+		if (!swrContext) {
+			LOG(("Audio Error: Unable to swr_alloc for file '%1', data size '%2'").arg(file.name()).arg(data.size()));
+			return false;
+		}
+		int64_t src_ch_layout = layout, dst_ch_layout = AudioToChannelLayout;
+		srcRate = freq;
+		AVSampleFormat src_sample_fmt = inputFormat, dst_sample_fmt = AudioToFormat;
+		dstRate = (freq != 44100 && freq != 48000) ? AudioVoiceMsgFrequency : freq;
+
+		av_opt_set_int(swrContext, "in_channel_layout", src_ch_layout, 0);
+		av_opt_set_int(swrContext, "in_sample_rate", srcRate, 0);
+		av_opt_set_sample_fmt(swrContext, "in_sample_fmt", src_sample_fmt, 0);
+		av_opt_set_int(swrContext, "out_channel_layout", dst_ch_layout, 0);
+		av_opt_set_int(swrContext, "out_sample_rate", dstRate, 0);
+		av_opt_set_sample_fmt(swrContext, "out_sample_fmt", dst_sample_fmt, 0);
+
+		if ((res = swr_init(swrContext)) < 0) {
+			LOG(("Audio Error: Unable to swr_init for 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)));
+			return false;
+		}
+
+		sampleSize = AudioToChannels * sizeof(short);
+		freq = dstRate;
+		len = av_rescale_rnd(len, dstRate, srcRate, AV_ROUND_UP);
+		fmt = AL_FORMAT_STEREO16;
+
+		maxResampleSamples = av_rescale_rnd(AVBlockSize / sampleSize, dstRate, srcRate, AV_ROUND_UP);
+		if ((res = av_samples_alloc_array_and_samples(&dstSamplesData, 0, AudioToChannels, maxResampleSamples, AudioToFormat, 0)) < 0) {
+			LOG(("Audio Error: Unable to av_samples_alloc for 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)));
+			return false;
+		}
+	}
+	if (position) {
+		int64 ts = (position * fmtContext->streams[streamId]->time_base.den) / (freq * fmtContext->streams[streamId]->time_base.num);
+		if (av_seek_frame(fmtContext, streamId, ts, AVSEEK_FLAG_ANY) < 0) {
+			if (av_seek_frame(fmtContext, streamId, ts, 0) < 0) {
+			}
+		}
+		//if (dstSamplesData) {
+		//	position = qRound(srcRate * (position / float64(dstRate)));
+		//}
+	}
+
+	return true;
+}
+
+AudioPlayerLoader::ReadResult FFMpegLoader::readMore(QByteArray &result, int64 &samplesAdded) {
+	int res;
+	if ((res = av_read_frame(fmtContext, &avpkt)) < 0) {
+		if (res != AVERROR_EOF) {
+			char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+			LOG(("Audio Error: Unable to av_read_frame() 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)));
+		}
+		return ReadResult::Error;
+	}
+	if (avpkt.stream_index == streamId) {
+		av_frame_unref(frame);
+		int got_frame = 0;
+		if ((res = avcodec_decode_audio4(codecContext, frame, &got_frame, &avpkt)) < 0) {
+			char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+			LOG(("Audio Error: Unable to avcodec_decode_audio4() 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)));
+
+			av_packet_unref(&avpkt);
+			if (res == AVERROR_INVALIDDATA) {
+				return ReadResult::NotYet; // try to skip bad packet
+			}
+			return ReadResult::Error;
+		}
+
+		if (got_frame) {
+			if (dstSamplesData) { // convert needed
+				int64_t dstSamples = av_rescale_rnd(swr_get_delay(swrContext, srcRate) + frame->nb_samples, dstRate, srcRate, AV_ROUND_UP);
+				if (dstSamples > maxResampleSamples) {
+					maxResampleSamples = dstSamples;
+					av_free(dstSamplesData[0]);
+
+					if ((res = av_samples_alloc(dstSamplesData, 0, AudioToChannels, maxResampleSamples, AudioToFormat, 1)) < 0) {
+						dstSamplesData[0] = 0;
+						char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+						LOG(("Audio Error: Unable to av_samples_alloc for 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)));
+
+						av_packet_unref(&avpkt);
+						return ReadResult::Error;
+					}
+				}
+				if ((res = swr_convert(swrContext, dstSamplesData, dstSamples, (const uint8_t**)frame->extended_data, frame->nb_samples)) < 0) {
+					char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+					LOG(("Audio Error: Unable to swr_convert for 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)));
+
+					av_packet_unref(&avpkt);
+					return ReadResult::Error;
+				}
+				int32 resultLen = av_samples_get_buffer_size(0, AudioToChannels, res, AudioToFormat, 1);
+				result.append((const char*)dstSamplesData[0], resultLen);
+				samplesAdded += resultLen / sampleSize;
+			} else {
+				result.append((const char*)frame->extended_data[0], frame->nb_samples * sampleSize);
+				samplesAdded += frame->nb_samples;
+			}
+		}
+	}
+	av_packet_unref(&avpkt);
+	return ReadResult::Ok;
+}
+
+FFMpegLoader::~FFMpegLoader() {
+	if (codecContext) avcodec_close(codecContext);
+	if (swrContext) swr_free(&swrContext);
+	if (dstSamplesData) {
+		if (dstSamplesData[0]) {
+			av_freep(&dstSamplesData[0]);
+		}
+		av_freep(&dstSamplesData);
+	}
+	av_frame_free(&frame);
+}
diff --git a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h
new file mode 100644
index 000000000..02ff3d3a3
--- /dev/null
+++ b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.h
@@ -0,0 +1,102 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+In addition, as a special exception, the copyright holders give permission
+to link the code of portions of this program with the OpenSSL library.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
+*/
+#pragma once
+
+#include "media/media_audio_loader.h"
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavutil/opt.h>
+#include <libswresample/swresample.h>
+} // extern "C"
+
+#include <AL/al.h>
+
+class AbstractFFMpegLoader : public AudioPlayerLoader {
+public:
+	AbstractFFMpegLoader(const FileLocation &file, const QByteArray &data) : AudioPlayerLoader(file, data) {
+	}
+
+	bool open(qint64 position = 0) override;
+
+	int64 duration() override {
+		return len;
+	}
+
+	int32 frequency() override {
+		return freq;
+	}
+
+	~AbstractFFMpegLoader();
+
+protected:
+	int32 freq = AudioVoiceMsgFrequency;
+	int64 len = 0;
+
+	uchar *ioBuffer = nullptr;
+	AVIOContext *ioContext = nullptr;
+	AVFormatContext *fmtContext = nullptr;
+	AVCodec *codec = nullptr;
+	int32 streamId = 0;
+
+	bool _opened = false;
+
+private:
+	static int _read_data(void *opaque, uint8_t *buf, int buf_size);
+	static int64_t _seek_data(void *opaque, int64_t offset, int whence);
+	static int _read_file(void *opaque, uint8_t *buf, int buf_size);
+	static int64_t _seek_file(void *opaque, int64_t offset, int whence);
+
+};
+
+class FFMpegLoader : public AbstractFFMpegLoader {
+public:
+	FFMpegLoader(const FileLocation &file, const QByteArray &data);
+
+	bool open(qint64 position = 0) override;
+
+	int32 format() override {
+		return fmt;
+	}
+
+	ReadResult readMore(QByteArray &result, int64 &samplesAdded) override;
+
+	~FFMpegLoader();
+
+protected:
+	int32 sampleSize = 2 * sizeof(uint16);
+
+private:
+	int32 fmt = AL_FORMAT_STEREO16;
+	int32 srcRate = AudioVoiceMsgFrequency;
+	int32 dstRate = AudioVoiceMsgFrequency;
+	int32 maxResampleSamples = 1024;
+	uint8_t **dstSamplesData = nullptr;
+
+	AVCodecContext *codecContext = nullptr;
+	AVPacket avpkt;
+	AVSampleFormat inputFormat;
+	AVFrame *frame = nullptr;
+
+	SwrContext *swrContext = nullptr;
+
+};
diff --git a/Telegram/SourceFiles/media/media_audio_loader.cpp b/Telegram/SourceFiles/media/media_audio_loader.cpp
new file mode 100644
index 000000000..1f1866bf2
--- /dev/null
+++ b/Telegram/SourceFiles/media/media_audio_loader.cpp
@@ -0,0 +1,80 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+In addition, as a special exception, the copyright holders give permission
+to link the code of portions of this program with the OpenSSL library.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
+*/
+#include "stdafx.h"
+#include "media/media_audio_loader.h"
+
+AudioPlayerLoader::AudioPlayerLoader(const FileLocation &file, const QByteArray &data)
+: file(file)
+, data(data) {
+}
+
+AudioPlayerLoader::~AudioPlayerLoader() {
+	if (access) {
+		file.accessDisable();
+		access = false;
+	}
+}
+
+bool AudioPlayerLoader::check(const FileLocation &file, const QByteArray &data) {
+	return this->file == file && this->data.size() == data.size();
+}
+
+void AudioPlayerLoader::saveDecodedSamples(QByteArray *samples, int64 *samplesCount) {
+	t_assert(_savedSamplesCount == 0);
+	t_assert(_savedSamples.isEmpty());
+	t_assert(!_holdsSavedSamples);
+	samples->swap(_savedSamples);
+	std::swap(*samplesCount, _savedSamplesCount);
+	_holdsSavedSamples = true;
+}
+
+void AudioPlayerLoader::takeSavedDecodedSamples(QByteArray *samples, int64 *samplesCount) {
+	t_assert(*samplesCount == 0);
+	t_assert(samples->isEmpty());
+	t_assert(_holdsSavedSamples);
+	samples->swap(_savedSamples);
+	std::swap(*samplesCount, _savedSamplesCount);
+	_holdsSavedSamples = false;
+}
+
+bool AudioPlayerLoader::holdsSavedDecodedSamples() const {
+	return _holdsSavedSamples;
+}
+
+bool AudioPlayerLoader::openFile() {
+	if (data.isEmpty()) {
+		if (f.isOpen()) f.close();
+		if (!access) {
+			if (!file.accessEnable()) {
+				LOG(("Audio Error: could not open file access '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(f.error()).arg(f.errorString()));
+				return false;
+			}
+			access = true;
+		}
+		f.setFileName(file.name());
+		if (!f.open(QIODevice::ReadOnly)) {
+			LOG(("Audio Error: could not open file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(f.error()).arg(f.errorString()));
+			return false;
+		}
+	}
+	dataPos = 0;
+	return true;
+}
diff --git a/Telegram/SourceFiles/media/media_audio_loader.h b/Telegram/SourceFiles/media/media_audio_loader.h
new file mode 100644
index 000000000..42d15a8e1
--- /dev/null
+++ b/Telegram/SourceFiles/media/media_audio_loader.h
@@ -0,0 +1,62 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+In addition, as a special exception, the copyright holders give permission
+to link the code of portions of this program with the OpenSSL library.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
+*/
+#pragma once
+
+class AudioPlayerLoader {
+public:
+	AudioPlayerLoader(const FileLocation &file, const QByteArray &data);
+	virtual ~AudioPlayerLoader();
+
+	virtual bool check(const FileLocation &file, const QByteArray &data);
+
+	virtual bool open(qint64 position = 0) = 0;
+	virtual int64 duration() = 0;
+	virtual int32 frequency() = 0;
+	virtual int32 format() = 0;
+
+	enum class ReadResult {
+		Error,
+		NotYet,
+		Ok,
+		Wait,
+	};
+	virtual ReadResult readMore(QByteArray &samples, int64 &samplesCount) = 0;
+
+	void saveDecodedSamples(QByteArray *samples, int64 *samplesCount);
+	void takeSavedDecodedSamples(QByteArray *samples, int64 *samplesCount);
+	bool holdsSavedDecodedSamples() const;
+
+protected:
+	FileLocation file;
+	bool access = false;
+	QByteArray data;
+
+	QFile f;
+	int32 dataPos = 0;
+
+	bool openFile();
+
+private:
+	QByteArray _savedSamples;
+	int64 _savedSamplesCount = 0;
+	bool _holdsSavedSamples = false;
+
+};
diff --git a/Telegram/SourceFiles/media/media_audio_loaders.cpp b/Telegram/SourceFiles/media/media_audio_loaders.cpp
new file mode 100644
index 000000000..c18e2e6e8
--- /dev/null
+++ b/Telegram/SourceFiles/media/media_audio_loaders.cpp
@@ -0,0 +1,423 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+In addition, as a special exception, the copyright holders give permission
+to link the code of portions of this program with the OpenSSL library.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
+*/
+#include "stdafx.h"
+#include "media/media_audio_loaders.h"
+
+#include "media/media_audio.h"
+#include "media/media_audio_ffmpeg_loader.h"
+#include "media/media_child_ffmpeg_loader.h"
+
+AudioPlayerLoaders::AudioPlayerLoaders(QThread *thread) : _fromVideoNotify(this, "onVideoSoundAdded") {
+	moveToThread(thread);
+}
+
+void AudioPlayerLoaders::feedFromVideo(VideoSoundPart &&part) {
+	bool invoke = true;
+	{
+		QMutexLocker lock(&_fromVideoMutex);
+		if (_fromVideoPlayId == part.videoPlayId) {
+			_fromVideoQueue.enqueue(*part.packet);
+		} else {
+			av_packet_unref(part.packet);
+			invoke = false;
+		}
+	}
+	if (invoke) {
+		_fromVideoNotify.call();
+	}
+}
+
+void AudioPlayerLoaders::startFromVideo(uint64 videoPlayId) {
+	QMutexLocker lock(&_fromVideoMutex);
+	_fromVideoPlayId = videoPlayId;
+	clearFromVideoQueue();
+}
+
+void AudioPlayerLoaders::stopFromVideo() {
+	startFromVideo(0);
+}
+
+void AudioPlayerLoaders::onVideoSoundAdded() {
+	bool waitingAndAdded = false;
+	{
+		QMutexLocker lock(&_fromVideoMutex);
+		if (_videoLoader && _videoLoader->playId() == _fromVideoPlayId && !_fromVideoQueue.isEmpty()) {
+			_videoLoader->enqueuePackets(_fromVideoQueue);
+			waitingAndAdded = _videoLoader->holdsSavedDecodedSamples();
+		}
+	}
+	if (waitingAndAdded) {
+		onLoad(_video);
+	}
+}
+
+AudioPlayerLoaders::~AudioPlayerLoaders() {
+	QMutexLocker lock(&_fromVideoMutex);
+	clearFromVideoQueue();
+}
+
+void AudioPlayerLoaders::clearFromVideoQueue() {
+	auto queue = createAndSwap(_fromVideoQueue);
+	for (auto &packet : queue) {
+		av_packet_unref(&packet);
+	}
+}
+
+void AudioPlayerLoaders::onInit() {
+}
+
+void AudioPlayerLoaders::onStart(const AudioMsgId &audio, qint64 position) {
+	auto type = audio.type();
+	clear(type);
+	{
+		QMutexLocker lock(internal::audioPlayerMutex());
+		AudioPlayer *voice = audioPlayer();
+		if (!voice) return;
+
+		auto data = voice->dataForType(type);
+		if (!data) return;
+
+		data->loading = true;
+	}
+
+	loadData(audio, position);
+}
+
+AudioMsgId AudioPlayerLoaders::clear(AudioMsgId::Type type) {
+	AudioMsgId result;
+	switch (type) {
+	case AudioMsgId::Type::Voice: std::swap(result, _audio); _audioLoader = nullptr; break;
+	case AudioMsgId::Type::Song: std::swap(result, _song); _songLoader = nullptr; break;
+	case AudioMsgId::Type::Video: std::swap(result, _video); _videoLoader = nullptr; break;
+	}
+	return result;
+}
+
+void AudioPlayerLoaders::setStoppedState(AudioPlayer::AudioMsg *m, AudioPlayerState state) {
+	m->state = state;
+	m->position = 0;
+}
+
+void AudioPlayerLoaders::emitError(AudioMsgId::Type type) {
+	emit error(clear(type));
+}
+
+void AudioPlayerLoaders::onLoad(const AudioMsgId &audio) {
+	loadData(audio, 0);
+}
+
+void AudioPlayerLoaders::loadData(const AudioMsgId &audio, qint64 position) {
+	SetupError err = SetupNoErrorStarted;
+	auto type = audio.type();
+	AudioPlayerLoader *l = setupLoader(audio, err, position);
+	if (!l) {
+		if (err == SetupErrorAtStart) {
+			emitError(type);
+		}
+		return;
+	}
+
+	bool started = (err == SetupNoErrorStarted);
+	bool finished = false;
+	bool waiting = false;
+	bool errAtStart = started;
+
+	QByteArray samples;
+	int64 samplesCount = 0;
+	if (l->holdsSavedDecodedSamples()) {
+		l->takeSavedDecodedSamples(&samples, &samplesCount);
+	}
+	while (samples.size() < AudioVoiceMsgBufferSize) {
+		auto res = l->readMore(samples, samplesCount);
+		using Result = AudioPlayerLoader::ReadResult;
+		if (res == Result::Error) {
+			if (errAtStart) {
+				{
+					QMutexLocker lock(internal::audioPlayerMutex());
+					AudioPlayer::AudioMsg *m = checkLoader(type);
+					if (m) m->state = AudioPlayerStoppedAtStart;
+				}
+				emitError(type);
+				return;
+			}
+			finished = true;
+			break;
+		} else if (res == Result::Ok) {
+			errAtStart = false;
+		} else if (res == Result::Wait) {
+			waiting = samples.isEmpty();// (samples.size() < AudioVoiceMsgBufferSize);
+			if (waiting) {
+				l->saveDecodedSamples(&samples, &samplesCount);
+			}
+			break;
+		}
+
+		QMutexLocker lock(internal::audioPlayerMutex());
+		if (!checkLoader(type)) {
+			clear(type);
+			return;
+		}
+	}
+
+	QMutexLocker lock(internal::audioPlayerMutex());
+	AudioPlayer::AudioMsg *m = checkLoader(type);
+	if (!m) {
+		clear(type);
+		return;
+	}
+
+	if (started) {
+		if (m->source) {
+			alSourceStop(m->source);
+			for (int32 i = 0; i < 3; ++i) {
+				if (m->samplesCount[i]) {
+					ALuint buffer = 0;
+					alSourceUnqueueBuffers(m->source, 1, &buffer);
+					m->samplesCount[i] = 0;
+				}
+			}
+			m->nextBuffer = 0;
+		}
+		m->skipStart = position;
+		m->skipEnd = m->duration - position;
+		m->position = 0;
+		m->started = 0;
+	}
+	if (samplesCount) {
+		if (!m->source) {
+			alGenSources(1, &m->source);
+			alSourcef(m->source, AL_PITCH, 1.f);
+			alSource3f(m->source, AL_POSITION, 0, 0, 0);
+			alSource3f(m->source, AL_VELOCITY, 0, 0, 0);
+			alSourcei(m->source, AL_LOOPING, 0);
+		}
+		if (!m->buffers[m->nextBuffer]) {
+			alGenBuffers(3, m->buffers);
+		}
+
+		// If this buffer is queued, try to unqueue some buffer.
+		if (m->samplesCount[m->nextBuffer]) {
+			ALint processed = 0;
+			alGetSourcei(m->source, AL_BUFFERS_PROCESSED, &processed);
+			if (processed < 1) { // No processed buffers, wait.
+				l->saveDecodedSamples(&samples, &samplesCount);
+				return;
+			}
+
+			// Unqueue some processed buffer.
+			ALuint buffer = 0;
+			alSourceUnqueueBuffers(m->source, 1, &buffer);
+			if (!internal::audioCheckError()) {
+				setStoppedState(m, AudioPlayerStoppedAtError);
+				emitError(type);
+				return;
+			}
+
+			// Find it in the list and make it the nextBuffer.
+			bool found = false;
+			for (int i = 0; i < 3; ++i) {
+				if (m->buffers[i] == buffer) {
+					found = true;
+					m->nextBuffer = i;
+					break;
+				}
+			}
+			if (!found) {
+				LOG(("Audio Error: Could not find the unqueued buffer! Buffer %1 in source %2 with processed count %3").arg(buffer).arg(m->source).arg(processed));
+				setStoppedState(m, AudioPlayerStoppedAtError);
+				emitError(type);
+				return;
+			}
+
+			if (m->samplesCount[m->nextBuffer]) {
+				m->skipStart += m->samplesCount[m->nextBuffer];
+				m->samplesCount[m->nextBuffer] = 0;
+			}
+		}
+
+		auto frequency = l->frequency();
+		auto format = l->format();
+		m->samplesCount[m->nextBuffer] = samplesCount;
+		alBufferData(m->buffers[m->nextBuffer], format, samples.constData(), samples.size(), frequency);
+
+		alSourceQueueBuffers(m->source, 1, m->buffers + m->nextBuffer);
+		m->skipEnd -= samplesCount;
+
+		m->nextBuffer = (m->nextBuffer + 1) % 3;
+
+		if (!internal::audioCheckError()) {
+			setStoppedState(m, AudioPlayerStoppedAtError);
+			emitError(type);
+			return;
+		}
+	} else {
+		if (waiting) {
+			return;
+		}
+		finished = true;
+	}
+
+	if (finished) {
+		m->skipEnd = 0;
+		m->duration = m->skipStart + m->samplesCount[0] + m->samplesCount[1] + m->samplesCount[2];
+		clear(type);
+	}
+
+	m->loading = false;
+	if (m->state == AudioPlayerResuming || m->state == AudioPlayerPlaying || m->state == AudioPlayerStarting) {
+		ALint state = AL_INITIAL;
+		alGetSourcei(m->source, AL_SOURCE_STATE, &state);
+		if (internal::audioCheckError()) {
+			if (state != AL_PLAYING) {
+				audioPlayer()->resumeDevice();
+
+				switch (type) {
+				case AudioMsgId::Type::Voice: alSourcef(m->source, AL_GAIN, internal::audioSuppressGain()); break;
+				case AudioMsgId::Type::Song: alSourcef(m->source, AL_GAIN, internal::audioSuppressSongGain() * cSongVolume()); break;
+				case AudioMsgId::Type::Video: alSourcef(m->source, AL_GAIN, internal::audioSuppressSongGain() * cSongVolume()); break;
+				}
+				if (!internal::audioCheckError()) {
+					setStoppedState(m, AudioPlayerStoppedAtError);
+					emitError(type);
+					return;
+				}
+
+				alSourcePlay(m->source);
+				if (!internal::audioCheckError()) {
+					setStoppedState(m, AudioPlayerStoppedAtError);
+					emitError(type);
+					return;
+				}
+
+				emit needToCheck();
+			}
+		} else {
+			setStoppedState(m, AudioPlayerStoppedAtError);
+			emitError(type);
+		}
+	}
+}
+
+AudioPlayerLoader *AudioPlayerLoaders::setupLoader(const AudioMsgId &audio, SetupError &err, qint64 position) {
+	err = SetupErrorAtStart;
+	QMutexLocker lock(internal::audioPlayerMutex());
+	AudioPlayer *voice = audioPlayer();
+	if (!voice) return nullptr;
+
+	auto data = voice->dataForType(audio.type());
+	if (!data || data->audio != audio || !data->loading) {
+		emit error(audio);
+		LOG(("Audio Error: trying to load part of audio, that is not current at the moment"));
+		err = SetupErrorNotPlaying;
+		return nullptr;
+	}
+
+	bool isGoodId = false;
+	AudioPlayerLoader *l = nullptr;
+	switch (audio.type()) {
+	case AudioMsgId::Type::Voice: l = _audioLoader.get(); isGoodId = (_audio == audio); break;
+	case AudioMsgId::Type::Song: l = _songLoader.get(); isGoodId = (_song == audio); break;
+	case AudioMsgId::Type::Video: l = _videoLoader.get(); isGoodId = (_song == audio); break;
+	}
+
+	if (l && (!isGoodId || !l->check(data->file, data->data))) {
+		clear(audio.type());
+	}
+
+	if (!l) {
+		std_::unique_ptr<AudioPlayerLoader> *loader = nullptr;
+		switch (audio.type()) {
+		case AudioMsgId::Type::Voice: _audio = audio; loader = &_audioLoader; break;
+		case AudioMsgId::Type::Song: _song = audio; loader = &_songLoader; break;
+		case AudioMsgId::Type::Video: _video = audio; break;
+		}
+
+		if (audio.type() == AudioMsgId::Type::Video) {
+			_videoLoader = std_::make_unique<ChildFFMpegLoader>(std_::move(data->videoData));
+			l = _videoLoader.get();
+		} else {
+			*loader = std_::make_unique<FFMpegLoader>(data->file, data->data);
+			l = loader->get();
+		}
+
+		if (!l->open(position)) {
+			data->state = AudioPlayerStoppedAtStart;
+			return nullptr;
+		}
+		int64 duration = l->duration();
+		if (duration <= 0) {
+			data->state = AudioPlayerStoppedAtStart;
+			return nullptr;
+		}
+		data->duration = duration;
+		data->frequency = l->frequency();
+		if (!data->frequency) data->frequency = AudioVoiceMsgFrequency;
+		err = SetupNoErrorStarted;
+	} else {
+		if (!data->skipEnd) {
+			err = SetupErrorLoadedFull;
+			LOG(("Audio Error: trying to load part of audio, that is already loaded to the end"));
+			return nullptr;
+		}
+	}
+	return l;
+}
+
+AudioPlayer::AudioMsg *AudioPlayerLoaders::checkLoader(AudioMsgId::Type type) {
+	AudioPlayer *voice = audioPlayer();
+	if (!voice) return 0;
+
+	auto data = voice->dataForType(type);
+	bool isGoodId = false;
+	AudioPlayerLoader *l = nullptr;
+	switch (type) {
+	case AudioMsgId::Type::Voice: l = _audioLoader.get(); isGoodId = (data->audio == _audio); break;
+	case AudioMsgId::Type::Song: l = _songLoader.get(); isGoodId = (data->audio == _song); break;
+	case AudioMsgId::Type::Video: l = _videoLoader.get(); isGoodId = (data->audio == _video); break;
+	}
+	if (!l || !data) return nullptr;
+
+	if (!isGoodId || !data->loading || !l->check(data->file, data->data)) {
+		LOG(("Audio Error: playing changed while loading"));
+		return nullptr;
+	}
+
+	return data;
+}
+
+void AudioPlayerLoaders::onCancel(const AudioMsgId &audio) {
+	switch (audio.type()) {
+	case AudioMsgId::Type::Voice: if (_audio == audio) clear(audio.type()); break;
+	case AudioMsgId::Type::Song: if (_song == audio) clear(audio.type()); break;
+	case AudioMsgId::Type::Video: if (_video == audio) clear(audio.type()); break;
+	}
+
+	QMutexLocker lock(internal::audioPlayerMutex());
+	AudioPlayer *voice = audioPlayer();
+	if (!voice) return;
+
+	for (int i = 0; i < AudioSimultaneousLimit; ++i) {
+		auto data = voice->dataForType(audio.type(), i);
+		if (data->audio == audio) {
+			data->loading = false;
+		}
+	}
+}
diff --git a/Telegram/SourceFiles/media/media_audio_loaders.h b/Telegram/SourceFiles/media/media_audio_loaders.h
new file mode 100644
index 000000000..f1893885a
--- /dev/null
+++ b/Telegram/SourceFiles/media/media_audio_loaders.h
@@ -0,0 +1,85 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+In addition, as a special exception, the copyright holders give permission
+to link the code of portions of this program with the OpenSSL library.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
+*/
+#pragma once
+
+#include "media/media_child_ffmpeg_loader.h"
+#include "media/media_audio.h"
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavutil/opt.h>
+#include <libswresample/swresample.h>
+} // extern "C"
+
+class AudioPlayerLoader;
+class ChildFFMpegLoader;
+class AudioPlayerLoaders : public QObject {
+	Q_OBJECT
+
+public:
+	AudioPlayerLoaders(QThread *thread);
+	void startFromVideo(uint64 videoPlayId);
+	void stopFromVideo();
+	void feedFromVideo(VideoSoundPart &&part);
+	~AudioPlayerLoaders();
+
+signals:
+	void error(const AudioMsgId &audio);
+	void needToCheck();
+
+public slots:
+	void onInit();
+
+	void onStart(const AudioMsgId &audio, qint64 position);
+	void onLoad(const AudioMsgId &audio);
+	void onCancel(const AudioMsgId &audio);
+
+	void onVideoSoundAdded();
+
+private:
+	void clearFromVideoQueue();
+
+	AudioMsgId _audio, _song, _video;
+	std_::unique_ptr<AudioPlayerLoader> _audioLoader;
+	std_::unique_ptr<AudioPlayerLoader> _songLoader;
+	std_::unique_ptr<ChildFFMpegLoader> _videoLoader;
+
+	QMutex _fromVideoMutex;
+	uint64 _fromVideoPlayId;
+	QQueue<AVPacket> _fromVideoQueue;
+	SingleDelayedCall _fromVideoNotify;
+
+	void emitError(AudioMsgId::Type type);
+	AudioMsgId clear(AudioMsgId::Type type);
+	void setStoppedState(AudioPlayer::AudioMsg *m, AudioPlayerState state = AudioPlayerStopped);
+
+	enum SetupError {
+		SetupErrorAtStart = 0,
+		SetupErrorNotPlaying = 1,
+		SetupErrorLoadedFull = 2,
+		SetupNoErrorStarted = 3,
+	};
+	void loadData(const AudioMsgId &audio, qint64 position);
+	AudioPlayerLoader *setupLoader(const AudioMsgId &audio, SetupError &err, qint64 position);
+	AudioPlayer::AudioMsg *checkLoader(AudioMsgId::Type type);
+
+};
diff --git a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp
new file mode 100644
index 000000000..1f122002e
--- /dev/null
+++ b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp
@@ -0,0 +1,186 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+In addition, as a special exception, the copyright holders give permission
+to link the code of portions of this program with the OpenSSL library.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
+*/
+#include "stdafx.h"
+#include "media/media_child_ffmpeg_loader.h"
+
+constexpr AVSampleFormat AudioToFormat = AV_SAMPLE_FMT_S16;
+constexpr int64_t AudioToChannelLayout = AV_CH_LAYOUT_STEREO;
+constexpr int32 AudioToChannels = 2;
+
+VideoSoundData::~VideoSoundData() {
+	if (context) {
+		avcodec_close(context);
+		avcodec_free_context(&context);
+		context = nullptr;
+	}
+}
+
+ChildFFMpegLoader::ChildFFMpegLoader(std_::unique_ptr<VideoSoundData> &&data) : AudioPlayerLoader(FileLocation(), QByteArray())
+, _parentData(std_::move(data)) {
+	_frame = av_frame_alloc();
+}
+
+bool ChildFFMpegLoader::open(qint64 position) {
+	int res = 0;
+	char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+
+	uint64_t layout = _parentData->context->channel_layout;
+	_inputFormat = _parentData->context->sample_fmt;
+	switch (layout) {
+	case AV_CH_LAYOUT_MONO:
+		switch (_inputFormat) {
+		case AV_SAMPLE_FMT_U8:
+		case AV_SAMPLE_FMT_U8P: _format = AL_FORMAT_MONO8; _sampleSize = 1; break;
+		case AV_SAMPLE_FMT_S16:
+		case AV_SAMPLE_FMT_S16P: _format = AL_FORMAT_MONO16; _sampleSize = sizeof(uint16); break;
+		default:
+			_sampleSize = -1; // convert needed
+		break;
+		}
+	break;
+	case AV_CH_LAYOUT_STEREO:
+		switch (_inputFormat) {
+		case AV_SAMPLE_FMT_U8: _format = AL_FORMAT_STEREO8; _sampleSize = 2; break;
+		case AV_SAMPLE_FMT_S16: _format = AL_FORMAT_STEREO16; _sampleSize = 2 * sizeof(uint16); break;
+		default:
+			_sampleSize = -1; // convert needed
+		break;
+		}
+	break;
+	default:
+		_sampleSize = -1; // convert needed
+	break;
+	}
+	if (_parentData->frequency != 44100 && _parentData->frequency != 48000) {
+		_sampleSize = -1; // convert needed
+	}
+
+	if (_sampleSize < 0) {
+		_swrContext = swr_alloc();
+		if (!_swrContext) {
+			LOG(("Audio Error: Unable to swr_alloc for file '%1', data size '%2'").arg(file.name()).arg(data.size()));
+			return false;
+		}
+		int64_t src_ch_layout = layout, dst_ch_layout = AudioToChannelLayout;
+		_srcRate = _parentData->frequency;
+		AVSampleFormat src_sample_fmt = _inputFormat, dst_sample_fmt = AudioToFormat;
+		_dstRate = (_parentData->frequency != 44100 && _parentData->frequency != 48000) ? AudioVoiceMsgFrequency : _parentData->frequency;
+
+		av_opt_set_int(_swrContext, "in_channel_layout", src_ch_layout, 0);
+		av_opt_set_int(_swrContext, "in_sample_rate", _srcRate, 0);
+		av_opt_set_sample_fmt(_swrContext, "in_sample_fmt", src_sample_fmt, 0);
+		av_opt_set_int(_swrContext, "out_channel_layout", dst_ch_layout, 0);
+		av_opt_set_int(_swrContext, "out_sample_rate", _dstRate, 0);
+		av_opt_set_sample_fmt(_swrContext, "out_sample_fmt", dst_sample_fmt, 0);
+
+		if ((res = swr_init(_swrContext)) < 0) {
+			LOG(("Audio Error: Unable to swr_init for 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)));
+			return false;
+		}
+
+		_sampleSize = AudioToChannels * sizeof(short);
+		_parentData->frequency = _dstRate;
+		_parentData->length = av_rescale_rnd(_parentData->length, _dstRate, _srcRate, AV_ROUND_UP);
+		_format = AL_FORMAT_STEREO16;
+
+		_maxResampleSamples = av_rescale_rnd(AVBlockSize / _sampleSize, _dstRate, _srcRate, AV_ROUND_UP);
+		if ((res = av_samples_alloc_array_and_samples(&_dstSamplesData, 0, AudioToChannels, _maxResampleSamples, AudioToFormat, 0)) < 0) {
+			LOG(("Audio Error: Unable to av_samples_alloc for 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)));
+			return false;
+		}
+	}
+
+	return true;
+}
+
+AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, int64 &samplesAdded) {
+	if (_queue.isEmpty()) {
+		return ReadResult::Wait;
+	}
+
+	av_frame_unref(_frame);
+	int got_frame = 0;
+	int res = 0;
+	auto packet = _queue.dequeue();
+	if ((res = avcodec_decode_audio4(_parentData->context, _frame, &got_frame, &packet)) < 0) {
+		char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+		LOG(("Audio Error: Unable to avcodec_decode_audio4() 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)));
+
+		av_packet_unref(&packet);
+		if (res == AVERROR_INVALIDDATA) {
+			return ReadResult::NotYet; // try to skip bad packet
+		}
+		return ReadResult::Error;
+	}
+
+	if (got_frame) {
+		if (_dstSamplesData) { // convert needed
+			int64_t dstSamples = av_rescale_rnd(swr_get_delay(_swrContext, _srcRate) + _frame->nb_samples, _dstRate, _srcRate, AV_ROUND_UP);
+			if (dstSamples > _maxResampleSamples) {
+				_maxResampleSamples = dstSamples;
+				av_free(_dstSamplesData[0]);
+
+				if ((res = av_samples_alloc(_dstSamplesData, 0, AudioToChannels, _maxResampleSamples, AudioToFormat, 1)) < 0) {
+					_dstSamplesData[0] = 0;
+					char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+					LOG(("Audio Error: Unable to av_samples_alloc for 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)));
+
+					av_packet_unref(&packet);
+					return ReadResult::Error;
+				}
+			}
+			if ((res = swr_convert(_swrContext, _dstSamplesData, dstSamples, (const uint8_t**)_frame->extended_data, _frame->nb_samples)) < 0) {
+				char err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+				LOG(("Audio Error: Unable to swr_convert for 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)));
+
+				av_packet_unref(&packet);
+				return ReadResult::Error;
+			}
+			int32 resultLen = av_samples_get_buffer_size(0, AudioToChannels, res, AudioToFormat, 1);
+			result.append((const char*)_dstSamplesData[0], resultLen);
+			samplesAdded += resultLen / _sampleSize;
+		} else {
+			result.append((const char*)_frame->extended_data[0], _frame->nb_samples * _sampleSize);
+			samplesAdded += _frame->nb_samples;
+		}
+	}
+	av_packet_unref(&packet);
+	return ReadResult::Ok;
+}
+
+void ChildFFMpegLoader::enqueuePackets(QQueue<AVPacket> &packets) {
+	_queue += std_::move(packets);
+	packets.clear();
+}
+
+ChildFFMpegLoader::~ChildFFMpegLoader() {
+	auto queue = createAndSwap(_queue);
+	for (auto &packet : queue) {
+		av_packet_unref(&packet);
+	}
+	if (_dstSamplesData) {
+		if (_dstSamplesData[0]) {
+			av_freep(&_dstSamplesData[0]);
+		}
+		av_freep(&_dstSamplesData);
+	}
+	av_frame_free(&_frame);
+}
diff --git a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h
new file mode 100644
index 000000000..fff2058c9
--- /dev/null
+++ b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h
@@ -0,0 +1,93 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+In addition, as a special exception, the copyright holders give permission
+to link the code of portions of this program with the OpenSSL library.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
+*/
+#pragma once
+
+#include "media/media_audio_loader.h"
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavutil/opt.h>
+#include <libswresample/swresample.h>
+} // extern "C"
+
+#include <AL/al.h>
+
+struct VideoSoundData {
+	uint64 videoPlayId = 0;
+	AVCodecContext *context = nullptr;
+	int32 frequency = AudioVoiceMsgFrequency;
+	int64 length = 0;
+	~VideoSoundData();
+};
+
+struct VideoSoundPart {
+	AVPacket *packet = nullptr;
+	uint64 videoPlayId = 0;
+};
+
+class ChildFFMpegLoader : public AudioPlayerLoader {
+public:
+	ChildFFMpegLoader(std_::unique_ptr<VideoSoundData> &&data);
+
+	bool open(qint64 position = 0) override;
+
+	bool check(const FileLocation &file, const QByteArray &data) override {
+		return true;
+	}
+
+	int32 format() override {
+		return _format;
+	}
+
+	int64 duration() override {
+		return _parentData->length;
+	}
+
+	int32 frequency() override {
+		return _parentData->frequency;
+	}
+
+	ReadResult readMore(QByteArray &result, int64 &samplesAdded) override;
+	void enqueuePackets(QQueue<AVPacket> &packets);
+
+	uint64 playId() const {
+		return _parentData->videoPlayId;
+	}
+
+	~ChildFFMpegLoader();
+
+private:
+	int32 _sampleSize = 2 * sizeof(uint16);
+	int32 _format = AL_FORMAT_STEREO16;
+	int32 _srcRate = AudioVoiceMsgFrequency;
+	int32 _dstRate = AudioVoiceMsgFrequency;
+	int32 _maxResampleSamples = 1024;
+	uint8_t **_dstSamplesData = nullptr;
+
+	std_::unique_ptr<VideoSoundData> _parentData;
+	AVSampleFormat _inputFormat;
+	AVFrame *_frame = nullptr;
+
+	SwrContext *_swrContext = nullptr;
+	QQueue<AVPacket> _queue;
+
+};
diff --git a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp
index f509310d8..cd20fd6d8 100644
--- a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp
+++ b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp
@@ -21,6 +21,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "stdafx.h"
 #include "media/media_clip_ffmpeg.h"
 
+#include "media/media_audio.h"
+#include "media/media_child_ffmpeg_loader.h"
+
 namespace Media {
 namespace Clip {
 namespace internal {
@@ -104,7 +107,12 @@ bool FFMpegReaderImplementation::readNextFrame() {
 		}
 
 		if (eofReached) {
+			if (_mode == Mode::Normal) {
+				return false;
+			}
+
 			clearPacketQueue();
+
 			if ((res = avformat_seek_file(_fmtContext, _streamId, std::numeric_limits<int64_t>::min(), 0, std::numeric_limits<int64_t>::max(), 0)) < 0) {
 				if ((res = av_seek_frame(_fmtContext, _streamId, 0, AVSEEK_FLAG_BYTE)) < 0) {
 					if ((res = av_seek_frame(_fmtContext, _streamId, 0, AVSEEK_FLAG_FRAME)) < 0) {
@@ -119,6 +127,7 @@ bool FFMpegReaderImplementation::readNextFrame() {
 			avcodec_flush_buffers(_codecContext);
 			_hadFrame = false;
 			_frameMs = 0;
+			_lastReadPacketMs = 0;
 		}
 	}
 
@@ -162,6 +171,16 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q
 		}
 	}
 
+	// Read some future packets for audio stream.
+	if (_audioStreamId) {
+		while (_frameMs + 5000 > _lastReadPacketMs) {
+			auto packetResult = readPacket();
+			if (packetResult != PacketResult::Ok) {
+				break;
+			}
+		}
+	}
+
 	av_frame_unref(_frame);
 	return true;
 }
@@ -171,6 +190,8 @@ int FFMpegReaderImplementation::nextFrameDelay() {
 }
 
 bool FFMpegReaderImplementation::start(Mode mode) {
+	_mode = mode;
+
 	initDevice();
 	if (!_device->open(QIODevice::ReadOnly)) {
 		LOG(("Gif Error: Unable to open device %1").arg(logData()));
@@ -207,12 +228,12 @@ bool FFMpegReaderImplementation::start(Mode mode) {
 	}
 	_packetNull.stream_index = _streamId;
 
-	// Get a pointer to the codec context for the audio stream
+	// Get a pointer to the codec context for the video stream
 	_codecContext = _fmtContext->streams[_streamId]->codec;
 	_codec = avcodec_find_decoder(_codecContext->codec_id);
 
 	_audioStreamId = av_find_best_stream(_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0);
-	if (mode == Mode::OnlyGifv) {
+	if (_mode == Mode::OnlyGifv) {
 		if (_audioStreamId >= 0) { // should be no audio stream
 			return false;
 		}
@@ -222,7 +243,7 @@ bool FFMpegReaderImplementation::start(Mode mode) {
 		if (_codecContext->codec_id != AV_CODEC_ID_H264) {
 			return false;
 		}
-	} else if (mode == Mode::Silent) {
+	} else if (_mode == Mode::Silent || !audioPlayer()) {
 		_audioStreamId = -1;
 	}
 	av_opt_set_int(_codecContext, "refcounted_frames", 1, 0);
@@ -231,6 +252,35 @@ bool FFMpegReaderImplementation::start(Mode mode) {
 		return false;
 	}
 
+	if (_audioStreamId >= 0) {
+		// Get a pointer to the codec context for the audio stream
+		auto audioContextOriginal = _fmtContext->streams[_audioStreamId]->codec;
+		auto audioCodec = avcodec_find_decoder(audioContextOriginal->codec_id);
+
+		AVCodecContext *audioContext = avcodec_alloc_context3(audioCodec);
+		if ((res = avcodec_copy_context(audioContext, audioContextOriginal)) != 0) {
+			LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
+			return false;
+		}
+		av_opt_set_int(audioContext, "refcounted_frames", 1, 0);
+		if ((res = avcodec_open2(audioContext, audioCodec, 0)) < 0) {
+			avcodec_free_context(&audioContext);
+			LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res)));
+			return false;
+		}
+
+		auto soundData = std_::make_unique<VideoSoundData>();
+		soundData->context = audioContext;
+		soundData->frequency = audioContextOriginal->sample_rate;
+		if (_fmtContext->streams[_audioStreamId]->duration == AV_NOPTS_VALUE) {
+			soundData->length = (_fmtContext->duration * soundData->frequency) / AV_TIME_BASE;
+		} else {
+			soundData->length = (_fmtContext->streams[_audioStreamId]->duration * soundData->frequency * _fmtContext->streams[_audioStreamId]->time_base.num) / _fmtContext->streams[_audioStreamId]->time_base.den;
+		}
+		soundData->videoPlayId = _playId = rand_value<uint64>();
+		audioPlayer()->playFromVideo(AudioMsgId(AudioMsgId::Type::Video), 0, std_::move(soundData));
+	}
+
 	return true;
 }
 
@@ -281,6 +331,10 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket(
 	bool videoPacket = (packet.stream_index == _streamId);
 	bool audioPacket = (_audioStreamId >= 0 && packet.stream_index == _audioStreamId);
 	if (audioPacket || videoPacket) {
+		int64 packetPts = (packet.pts == AV_NOPTS_VALUE) ? packet.dts : packet.pts;
+		int64 packetMs = (packetPts * 1000LL * _fmtContext->streams[packet.stream_index]->time_base.num) / _fmtContext->streams[packet.stream_index]->time_base.den;
+		_lastReadPacketMs = packetMs;
+
 		//AVPacket packetForQueue;
 		//av_init_packet(&packetForQueue);
 		//if ((res = av_packet_ref(&packetForQueue, &packet)) < 0) {
@@ -294,9 +348,10 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket(
 			//_packetQueue.enqueue(packetForQueue);
 		} else if (audioPacket) {
 			// queue packet to audio player
-			// audioPlayer()->enqueuePacket(packet, &isEnough)
-			//av_packet_unref(&packetForQueue);
-			av_packet_unref(&packet);
+			VideoSoundPart part;
+			part.packet = &packet;
+			part.videoPlayId = _playId;
+			audioPlayer()->feedFromVideo(std_::move(part));
 		}
 	} else {
 		av_packet_unref(&packet);
diff --git a/Telegram/SourceFiles/media/media_clip_ffmpeg.h b/Telegram/SourceFiles/media/media_clip_ffmpeg.h
index 78c548282..02f5f18e6 100644
--- a/Telegram/SourceFiles/media/media_clip_ffmpeg.h
+++ b/Telegram/SourceFiles/media/media_clip_ffmpeg.h
@@ -62,6 +62,8 @@ private:
 	static int _read(void *opaque, uint8_t *buf, int buf_size);
 	static int64_t _seek(void *opaque, int64_t offset, int whence);
 
+	Mode _mode = Mode::Normal;
+
 	uchar *_ioBuffer = nullptr;
 	AVIOContext *_ioContext = nullptr;
 	AVFormatContext *_fmtContext = nullptr;
@@ -74,6 +76,8 @@ private:
 	bool _frameRead = false;
 
 	int _audioStreamId = 0;
+	uint64 _playId = 0;
+	int64 _lastReadPacketMs = 0;
 
 	QQueue<AVPacket> _packetQueue;
 	AVPacket _packetNull; // for final decoding
diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp
index 6c05b881f..d2accd161 100644
--- a/Telegram/SourceFiles/overview/overview_layout.cpp
+++ b/Telegram/SourceFiles/overview/overview_layout.cpp
@@ -31,7 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "fileuploader.h"
 #include "mainwindow.h"
 #include "playerwidget.h"
-#include "audio.h"
+#include "media/media_audio.h"
 #include "localstorage.h"
 
 namespace Overview {
diff --git a/Telegram/SourceFiles/playerwidget.cpp b/Telegram/SourceFiles/playerwidget.cpp
index 29f7941ce..40e3ae804 100644
--- a/Telegram/SourceFiles/playerwidget.cpp
+++ b/Telegram/SourceFiles/playerwidget.cpp
@@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "playerwidget.h"
 #include "mainwidget.h"
 #include "localstorage.h"
-#include "audio.h"
+#include "media/media_audio.h"
 
 PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent)
 , _a_state(animation(this, &PlayerWidget::step_state))
diff --git a/Telegram/SourceFiles/playerwidget.h b/Telegram/SourceFiles/playerwidget.h
index 72aa9ba98..c8e893483 100644
--- a/Telegram/SourceFiles/playerwidget.h
+++ b/Telegram/SourceFiles/playerwidget.h
@@ -20,7 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 */
 #pragma once
 
-#include "audio.h"
+#include "media/media_audio.h"
 
 class PlayerWidget : public TWidget {
 	Q_OBJECT
diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp
index 563af77fb..8519bd95c 100644
--- a/Telegram/SourceFiles/structs.cpp
+++ b/Telegram/SourceFiles/structs.cpp
@@ -32,7 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "ui/filedialog.h"
 #include "apiwrap.h"
 #include "boxes/confirmbox.h"
-#include "audio.h"
+#include "media/media_audio.h"
 #include "localstorage.h"
 
 namespace {
diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h
index 379fd0c18..3826aa5f0 100644
--- a/Telegram/SourceFiles/structs.h
+++ b/Telegram/SourceFiles/structs.h
@@ -1241,10 +1241,12 @@ public:
 	AudioMsgId() {
 	}
 	AudioMsgId(DocumentData *audio, const FullMsgId &msgId) : _audio(audio), _contextId(msgId) {
-		setType();
+		setTypeFromAudio();
 	}
 	AudioMsgId(DocumentData *audio, ChannelId channelId, MsgId msgId) : _audio(audio), _contextId(channelId, msgId) {
-		setType();
+		setTypeFromAudio();
+	}
+	AudioMsgId(Type type) : _type(type) {
 	}
 
 	Type type() const {
@@ -1262,7 +1264,7 @@ public:
 	}
 
 private:
-	void setType() {
+	void setTypeFromAudio() {
 		if (_audio->voice()) {
 			_type = Type::Voice;
 		} else if (_audio->song()) {
diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h
index 2fe3a169e..aa6701a43 100644
--- a/Telegram/SourceFiles/ui/twidget.h
+++ b/Telegram/SourceFiles/ui/twidget.h
@@ -265,23 +265,23 @@ class SingleDelayedCall : public QObject {
 	Q_OBJECT
 
 public:
-	SingleDelayedCall(QObject *parent, const char *member) : QObject(parent), _pending(false), _member(member) {
+	SingleDelayedCall(QObject *parent, const char *member) : QObject(parent), _member(member) {
 	}
 	void call() {
-		if (!_pending) {
-			_pending = true;
+		if (!_pending.loadAcquire()) {
+			_pending.storeRelease(1);
 			QMetaObject::invokeMethod(this, "makeDelayedCall", Qt::QueuedConnection);
 		}
 	}
 
 private slots:
 	void makeDelayedCall() {
-		_pending = false;
+		_pending.storeRelease(0);
 		QMetaObject::invokeMethod(parent(), _member);
 	}
 
 private:
-	bool _pending;
+	QAtomicInt _pending = { 0 };
 	const char *_member;
 
 };
diff --git a/Telegram/Telegram.pro b/Telegram/Telegram.pro
index 8651364b3..1fbe514a7 100644
--- a/Telegram/Telegram.pro
+++ b/Telegram/Telegram.pro
@@ -104,7 +104,6 @@ SOURCES += \
     ./SourceFiles/apiwrap.cpp \
     ./SourceFiles/app.cpp \
     ./SourceFiles/application.cpp \
-    ./SourceFiles/audio.cpp \
     ./SourceFiles/autoupdater.cpp \
     ./SourceFiles/dialogswidget.cpp \
     ./SourceFiles/dropdown.cpp \
@@ -172,6 +171,11 @@ SOURCES += \
     ./SourceFiles/intro/intropwdcheck.cpp \
     ./SourceFiles/intro/introsignup.cpp \
     ./SourceFiles/intro/introstart.cpp \
+    ./SourceFiles/media/media_audio.cpp \
+    ./SourceFiles/media/media_clip_ffmpeg.cpp \
+    ./SourceFiles/media/media_clip_implementation.cpp \
+    ./SourceFiles/media/media_clip_qtgif.cpp \
+    ./SourceFiles/media/media_clip_reader.cpp \
     ./SourceFiles/mtproto/facade.cpp \
     ./SourceFiles/mtproto/auth_key.cpp \
     ./SourceFiles/mtproto/connection.cpp \
@@ -254,7 +258,6 @@ HEADERS += \
     ./SourceFiles/apiwrap.h \
     ./SourceFiles/app.h \
     ./SourceFiles/application.h \
-    ./SourceFiles/audio.h \
     ./SourceFiles/autoupdater.h \
     ./SourceFiles/config.h \
     ./SourceFiles/countries.h \
@@ -328,6 +331,11 @@ HEADERS += \
     ./SourceFiles/intro/intropwdcheck.h \
     ./SourceFiles/intro/introsignup.h \
     ./SourceFiles/intro/introstart.h \
+    ./SourceFiles/media/media_audio.h \
+    ./SourceFiles/media/media_clip_ffmpeg.h \
+    ./SourceFiles/media/media_clip_implementation.h \
+    ./SourceFiles/media/media_clip_qtgif.h \
+    ./SourceFiles/media/media_clip_reader.h \
     ./SourceFiles/mtproto/facade.h \
     ./SourceFiles/mtproto/auth_key.h \
     ./SourceFiles/mtproto/connection.h \
diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj
index 532a644d2..42dc178c9 100644
--- a/Telegram/Telegram.vcxproj
+++ b/Telegram/Telegram.vcxproj
@@ -186,10 +186,6 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="GeneratedFiles\Debug\moc_audio.cpp">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
-    </ClCompile>
     <ClCompile Include="GeneratedFiles\Debug\moc_autolockbox.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -371,6 +367,14 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_media_audio.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_media_audio_loaders.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Debug\moc_media_clip_reader.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -515,10 +519,6 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="GeneratedFiles\Deploy\moc_audio.cpp">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
-    </ClCompile>
     <ClCompile Include="GeneratedFiles\Deploy\moc_autolockbox.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -705,6 +705,14 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Deploy\moc_media_audio.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Deploy\moc_media_audio_loaders.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Deploy\moc_media_clip_reader.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -880,10 +888,6 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="GeneratedFiles\Release\moc_audio.cpp">
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
-      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
-    </ClCompile>
     <ClCompile Include="GeneratedFiles\Release\moc_autolockbox.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@@ -1070,6 +1074,14 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_media_audio.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_media_audio_loaders.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Release\moc_media_clip_reader.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@@ -1204,7 +1216,6 @@
     <ClCompile Include="SourceFiles\apiwrap.cpp" />
     <ClCompile Include="SourceFiles\app.cpp" />
     <ClCompile Include="SourceFiles\application.cpp" />
-    <ClCompile Include="SourceFiles\audio.cpp" />
     <ClCompile Include="SourceFiles\autoupdater.cpp" />
     <ClCompile Include="SourceFiles\boxes\aboutbox.cpp" />
     <ClCompile Include="SourceFiles\boxes\abstractbox.cpp" />
@@ -1262,6 +1273,11 @@
     <ClCompile Include="SourceFiles\main.cpp" />
     <ClCompile Include="SourceFiles\mainwidget.cpp" />
     <ClCompile Include="SourceFiles\mediaview.cpp" />
+    <ClCompile Include="SourceFiles\media\media_audio.cpp" />
+    <ClCompile Include="SourceFiles\media\media_audio_ffmpeg_loader.cpp" />
+    <ClCompile Include="SourceFiles\media\media_audio_loader.cpp" />
+    <ClCompile Include="SourceFiles\media\media_audio_loaders.cpp" />
+    <ClCompile Include="SourceFiles\media\media_child_ffmpeg_loader.cpp" />
     <ClCompile Include="SourceFiles\media\media_clip_ffmpeg.cpp" />
     <ClCompile Include="SourceFiles\media\media_clip_implementation.cpp" />
     <ClCompile Include="SourceFiles\media\media_clip_qtgif.cpp" />
@@ -1538,6 +1554,37 @@
       <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
       <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/media/media_clip_reader.h"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include"</Command>
     </CustomBuild>
+    <CustomBuild Include="SourceFiles\media\media_audio.h">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing media_audio.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/media/media_audio.h"  -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing media_audio.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/media/media_audio.h"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing media_audio.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/media/media_audio.h"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include"</Command>
+    </CustomBuild>
+    <ClInclude Include="SourceFiles\media\media_audio_ffmpeg_loader.h" />
+    <ClInclude Include="SourceFiles\media\media_audio_loader.h" />
+    <CustomBuild Include="SourceFiles\media\media_audio_loaders.h">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing media_audio_loaders.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/media/media_audio_loaders.h"  -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing media_audio_loaders.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/media/media_audio_loaders.h"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing media_audio_loaders.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/media/media_audio_loaders.h"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include"</Command>
+    </CustomBuild>
+    <ClInclude Include="SourceFiles\media\media_child_ffmpeg_loader.h" />
     <ClInclude Include="SourceFiles\media\media_clip_ffmpeg.h" />
     <ClInclude Include="SourceFiles\media\media_clip_implementation.h" />
     <ClInclude Include="SourceFiles\media\media_clip_qtgif.h" />
@@ -2252,20 +2299,6 @@
       <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
       <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
     </CustomBuild>
-    <CustomBuild Include="SourceFiles\audio.h">
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing audio.h...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS  "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/audio.h"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing audio.h...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS  "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include" "-fstdafx.h" "-f../../SourceFiles/audio.h"</Command>
-      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
-      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing audio.h...</Message>
-      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
-      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS  "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/audio.h"</Command>
-    </CustomBuild>
     <CustomBuild Include="SourceFiles\boxes\usernamebox.h">
       <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
       <Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing usernamebox.h...</Message>
diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters
index 64116e19b..6993d85e1 100644
--- a/Telegram/Telegram.vcxproj.filters
+++ b/Telegram/Telegram.vcxproj.filters
@@ -447,18 +447,6 @@
     <ClCompile Include="GeneratedFiles\Release\moc_overviewwidget.cpp">
       <Filter>GeneratedFiles\Release</Filter>
     </ClCompile>
-    <ClCompile Include="SourceFiles\audio.cpp">
-      <Filter>SourceFiles</Filter>
-    </ClCompile>
-    <ClCompile Include="GeneratedFiles\Deploy\moc_audio.cpp">
-      <Filter>GeneratedFiles\Deploy</Filter>
-    </ClCompile>
-    <ClCompile Include="GeneratedFiles\Debug\moc_audio.cpp">
-      <Filter>GeneratedFiles\Debug</Filter>
-    </ClCompile>
-    <ClCompile Include="GeneratedFiles\Release\moc_audio.cpp">
-      <Filter>GeneratedFiles\Release</Filter>
-    </ClCompile>
     <ClCompile Include="SourceFiles\boxes\usernamebox.cpp">
       <Filter>SourceFiles\boxes</Filter>
     </ClCompile>
@@ -1368,6 +1356,39 @@
     <ClCompile Include="SourceFiles\media\media_clip_qtgif.cpp">
       <Filter>SourceFiles\media</Filter>
     </ClCompile>
+    <ClCompile Include="SourceFiles\media\media_audio.cpp">
+      <Filter>SourceFiles\media</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Deploy\moc_media_audio.cpp">
+      <Filter>GeneratedFiles\Deploy</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_media_audio.cpp">
+      <Filter>GeneratedFiles\Debug</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_media_audio.cpp">
+      <Filter>GeneratedFiles\Release</Filter>
+    </ClCompile>
+    <ClCompile Include="SourceFiles\media\media_child_ffmpeg_loader.cpp">
+      <Filter>SourceFiles\media</Filter>
+    </ClCompile>
+    <ClCompile Include="SourceFiles\media\media_audio_ffmpeg_loader.cpp">
+      <Filter>SourceFiles\media</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Deploy\moc_media_audio_loaders.cpp">
+      <Filter>GeneratedFiles\Deploy</Filter>
+    </ClCompile>
+    <ClCompile Include="SourceFiles\media\media_audio_loaders.cpp">
+      <Filter>SourceFiles\media</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_media_audio_loaders.cpp">
+      <Filter>GeneratedFiles\Debug</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_media_audio_loaders.cpp">
+      <Filter>GeneratedFiles\Release</Filter>
+    </ClCompile>
+    <ClCompile Include="SourceFiles\media\media_audio_loader.cpp">
+      <Filter>SourceFiles\media</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="SourceFiles\stdafx.h">
@@ -1628,6 +1649,15 @@
     <ClInclude Include="SourceFiles\media\media_clip_qtgif.h">
       <Filter>SourceFiles\media</Filter>
     </ClInclude>
+    <ClInclude Include="SourceFiles\media\media_child_ffmpeg_loader.h">
+      <Filter>SourceFiles\media</Filter>
+    </ClInclude>
+    <ClInclude Include="SourceFiles\media\media_audio_loader.h">
+      <Filter>SourceFiles\media</Filter>
+    </ClInclude>
+    <ClInclude Include="SourceFiles\media\media_audio_ffmpeg_loader.h">
+      <Filter>SourceFiles\media</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="SourceFiles\application.h">
@@ -1705,9 +1735,6 @@
     <CustomBuild Include="SourceFiles\overviewwidget.h">
       <Filter>SourceFiles</Filter>
     </CustomBuild>
-    <CustomBuild Include="SourceFiles\audio.h">
-      <Filter>SourceFiles</Filter>
-    </CustomBuild>
     <CustomBuild Include="SourceFiles\boxes\usernamebox.h">
       <Filter>SourceFiles\boxes</Filter>
     </CustomBuild>
@@ -1921,6 +1948,12 @@
     <CustomBuild Include="SourceFiles\media\media_clip_reader.h">
       <Filter>SourceFiles\media</Filter>
     </CustomBuild>
+    <CustomBuild Include="SourceFiles\media\media_audio.h">
+      <Filter>SourceFiles\media</Filter>
+    </CustomBuild>
+    <CustomBuild Include="SourceFiles\media\media_audio_loaders.h">
+      <Filter>SourceFiles\media</Filter>
+    </CustomBuild>
   </ItemGroup>
   <ItemGroup>
     <None Include="Resources\langs\lang_it.strings">
diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj
index 449edf2cf..817d47551 100644
--- a/Telegram/Telegram.xcodeproj/project.pbxproj
+++ b/Telegram/Telegram.xcodeproj/project.pbxproj
@@ -163,8 +163,8 @@
 		07C8FE101CB80890007A8702 /* toast.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C8FE0C1CB80890007A8702 /* toast.cpp */; };
 		07C8FE121CB80915007A8702 /* moc_toast_manager.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07C8FE111CB80915007A8702 /* moc_toast_manager.cpp */; };
 		07CAACD81AEA64F00058E508 /* AudioUnit.framework in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 07CAACD71AEA64F00058E508 /* AudioUnit.framework */; };
-		07D7034B19B8755A00C4EED2 /* audio.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07D7034919B8755A00C4EED2 /* audio.cpp */; };
-		07D703BB19B88FB900C4EED2 /* moc_audio.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07D703BA19B88FB900C4EED2 /* moc_audio.cpp */; };
+		07D7034B19B8755A00C4EED2 /* media_audio.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07D7034919B8755A00C4EED2 /* media_audio.cpp */; };
+		07D703BB19B88FB900C4EED2 /* moc_media_audio.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07D703BA19B88FB900C4EED2 /* moc_media_audio.cpp */; };
 		07D7954A1B5544B200DE9598 /* qtpcre in Link Binary With Libraries */ = {isa = PBXBuildFile; fileRef = 07D795491B5544B200DE9598 /* qtpcre */; };
 		07D7EABA1A597DD000838BA2 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 07D7EABC1A597DD000838BA2 /* Localizable.strings */; };
 		07D8509419F5C97E00623D75 /* core_types.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07D8509219F5C97E00623D75 /* core_types.cpp */; };
@@ -603,9 +603,9 @@
 		07C8FE111CB80915007A8702 /* moc_toast_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_toast_manager.cpp; path = GeneratedFiles/Debug/moc_toast_manager.cpp; sourceTree = SOURCE_ROOT; };
 		07CAACD71AEA64F00058E508 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
 		07D518D41CD0E27600F5FF59 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = version.h; path = SourceFiles/core/version.h; sourceTree = SOURCE_ROOT; };
-		07D7034919B8755A00C4EED2 /* audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = audio.cpp; path = SourceFiles/audio.cpp; sourceTree = SOURCE_ROOT; };
-		07D7034A19B8755A00C4EED2 /* audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = audio.h; path = SourceFiles/audio.h; sourceTree = SOURCE_ROOT; };
-		07D703BA19B88FB900C4EED2 /* moc_audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_audio.cpp; path = GeneratedFiles/Debug/moc_audio.cpp; sourceTree = SOURCE_ROOT; };
+		07D7034919B8755A00C4EED2 /* media_audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = media_audio.cpp; path = SourceFiles/media/media_audio.cpp; sourceTree = SOURCE_ROOT; };
+		07D7034A19B8755A00C4EED2 /* media_audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = media_audio.h; path = SourceFiles/media/media_audio.h; sourceTree = SOURCE_ROOT; };
+		07D703BA19B88FB900C4EED2 /* moc_media_audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_media_audio.cpp; path = GeneratedFiles/Debug/moc_media_audio.cpp; sourceTree = SOURCE_ROOT; };
 		07D795491B5544B200DE9598 /* qtpcre */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = qtpcre; path = "$(QT_PATH)/lib/libqtpcre$(QT_LIBRARY_SUFFIX).a"; sourceTree = "<group>"; };
 		07D7EABB1A597DD000838BA2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Resources/langs/en.lproj/Localizable.strings; sourceTree = "<group>"; };
 		07D7EABD1A597DD200838BA2 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = Resources/langs/es.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -1413,8 +1413,8 @@
 				C19DF71B273A4843553518F2 /* app.h */,
 				C20F9DD8C7B031B8E20D5653 /* application.cpp */,
 				09FD01F2BD652EB838A296D8 /* application.h */,
-				07D7034919B8755A00C4EED2 /* audio.cpp */,
-				07D7034A19B8755A00C4EED2 /* audio.h */,
+				07D7034919B8755A00C4EED2 /* media_audio.cpp */,
+				07D7034A19B8755A00C4EED2 /* media_audio.h */,
 				07C7596D1B1F7E0000662169 /* autoupdater.cpp */,
 				07C7596E1B1F7E0000662169 /* autoupdater.h */,
 				206B4F5CBD5354BCE19FF32F /* countries.h */,
@@ -1500,7 +1500,7 @@
 				A1479F94376F9732B57C69DB /* moc_animation.cpp */,
 				0764D55C1ABAD71B00FBFEED /* moc_apiwrap.cpp */,
 				E181C525E21A16F2D4396CA7 /* moc_application.cpp */,
-				07D703BA19B88FB900C4EED2 /* moc_audio.cpp */,
+				07D703BA19B88FB900C4EED2 /* moc_media_audio.cpp */,
 				07DE92A91AA4928200A18F6F /* moc_autolockbox.cpp */,
 				07C759711B1F7E2800662169 /* moc_autoupdater.cpp */,
 				078A2FC91A811C5900CCC7A0 /* moc_backgroundbox.cpp */,
@@ -2013,7 +2013,7 @@
 				EBE29731916DB43BF49FE7A4 /* aboutbox.cpp in Compile Sources */,
 				4426AF526AAD86D6F73CE36F /* addcontactbox.cpp in Compile Sources */,
 				0716C9561D0589A700797B22 /* profile_userpic_button.cpp in Compile Sources */,
-				07D7034B19B8755A00C4EED2 /* audio.cpp in Compile Sources */,
+				07D7034B19B8755A00C4EED2 /* media_audio.cpp in Compile Sources */,
 				A0A6B97F7DBEC81004EC9461 /* confirmbox.cpp in Compile Sources */,
 				4FEA8F51B7BC7CAC71347A1A /* connectionbox.cpp in Compile Sources */,
 				07C7596F1B1F7E0000662169 /* autoupdater.cpp in Compile Sources */,
@@ -2024,7 +2024,7 @@
 				07C8FE0F1CB80890007A8702 /* toast_widget.cpp in Compile Sources */,
 				0716C9741D058C8600797B22 /* moc_profile_inner_widget.cpp in Compile Sources */,
 				3ABE4F9B2264F770D944106D /* emojibox.cpp in Compile Sources */,
-				07D703BB19B88FB900C4EED2 /* moc_audio.cpp in Compile Sources */,
+				07D703BB19B88FB900C4EED2 /* moc_media_audio.cpp in Compile Sources */,
 				77B998AC22A13EF3DDEE07AC /* photocropbox.cpp in Compile Sources */,
 				F278C423357CA99797EA30AB /* photosendbox.cpp in Compile Sources */,
 				E8D95529CED88F18818C9A8B /* introwidget.cpp in Compile Sources */,
diff --git a/Telegram/Telegram.xcodeproj/qt_preprocess.mak b/Telegram/Telegram.xcodeproj/qt_preprocess.mak
index 0707e159e..2cc42ee18 100644
--- a/Telegram/Telegram.xcodeproj/qt_preprocess.mak
+++ b/Telegram/Telegram.xcodeproj/qt_preprocess.mak
@@ -55,7 +55,6 @@ compilers: GeneratedFiles/qrc_telegram.cpp\
 	 GeneratedFiles/Debug/moc_animation.cpp\
 	 GeneratedFiles/Debug/moc_apiwrap.cpp\
 	 GeneratedFiles/Debug/moc_application.cpp\
-	 GeneratedFiles/Debug/moc_audio.cpp\
 	 GeneratedFiles/Debug/moc_autolockbox.cpp\
 	 GeneratedFiles/Debug/moc_autoupdater.cpp\
 	 GeneratedFiles/Debug/moc_backgroundbox.cpp\
@@ -99,6 +98,8 @@ compilers: GeneratedFiles/qrc_telegram.cpp\
 	 GeneratedFiles/Debug/moc_main_window_mac.cpp\
 	 GeneratedFiles/Debug/moc_mainwidget.cpp\
 	 GeneratedFiles/Debug/moc_mainwindow.cpp\
+	 GeneratedFiles/Debug/moc_media_audio.cpp\
+	 GeneratedFiles/Debug/moc_media_clip_reader.cpp\
 	 GeneratedFiles/Debug/moc_mediaview.cpp\
 	 GeneratedFiles/Debug/moc_overviewwidget.cpp\
 	 GeneratedFiles/Debug/moc_passcodebox.cpp\
@@ -193,7 +194,6 @@ compiler_moc_header_make_all: GeneratedFiles/Debug/moc_aboutbox.cpp\
 	 GeneratedFiles/Debug/moc_animation.cpp\
 	 GeneratedFiles/Debug/moc_apiwrap.cpp\
 	 GeneratedFiles/Debug/moc_application.cpp\
-	 GeneratedFiles/Debug/moc_audio.cpp\
 	 GeneratedFiles/Debug/moc_autolockbox.cpp\
 	 GeneratedFiles/Debug/moc_autoupdater.cpp\
 	 GeneratedFiles/Debug/moc_backgroundbox.cpp\
@@ -237,6 +237,8 @@ compiler_moc_header_make_all: GeneratedFiles/Debug/moc_aboutbox.cpp\
 	 GeneratedFiles/Debug/moc_main_window_mac.cpp\
 	 GeneratedFiles/Debug/moc_mainwidget.cpp\
 	 GeneratedFiles/Debug/moc_mainwindow.cpp\
+	 GeneratedFiles/Debug/moc_media_audio.cpp\
+	 GeneratedFiles/Debug/moc_media_clip_reader.cpp\
 	 GeneratedFiles/Debug/moc_mediaview.cpp\
 	 GeneratedFiles/Debug/moc_overviewwidget.cpp\
 	 GeneratedFiles/Debug/moc_passcodebox.cpp\
@@ -274,7 +276,6 @@ compiler_moc_header_clean:
 	 GeneratedFiles/Debug/moc_animation.cpp\
 	 GeneratedFiles/Debug/moc_apiwrap.cpp\
 	 GeneratedFiles/Debug/moc_application.cpp\
-	 GeneratedFiles/Debug/moc_audio.cpp\
 	 GeneratedFiles/Debug/moc_autolockbox.cpp\
 	 GeneratedFiles/Debug/moc_autoupdater.cpp\
 	 GeneratedFiles/Debug/moc_backgroundbox.cpp\
@@ -318,6 +319,8 @@ compiler_moc_header_clean:
 	 GeneratedFiles/Debug/moc_main_window_mac.cpp\
 	 GeneratedFiles/Debug/moc_mainwidget.cpp\
 	 GeneratedFiles/Debug/moc_mainwindow.cpp\
+	 GeneratedFiles/Debug/moc_media_audio.cpp\
+	 GeneratedFiles/Debug/moc_media_clip_reader.cpp\
 	 GeneratedFiles/Debug/moc_mediaview.cpp\
 	 GeneratedFiles/Debug/moc_overviewwidget.cpp\
 	 GeneratedFiles/Debug/moc_passcodebox.cpp\
@@ -366,9 +369,6 @@ GeneratedFiles/Debug/moc_apiwrap.cpp: SourceFiles/apiwrap.h
 GeneratedFiles/Debug/moc_application.cpp: SourceFiles/application.h
 	$(MOC_FILE) SourceFiles/application.h -o GeneratedFiles/Debug/moc_application.cpp
 
-GeneratedFiles/Debug/moc_audio.cpp: SourceFiles/audio.h
-	$(MOC_FILE) SourceFiles/audio.h -o GeneratedFiles/Debug/moc_audio.cpp
-
 GeneratedFiles/Debug/moc_autolockbox.cpp: SourceFiles/boxes/autolockbox.h
 	$(MOC_FILE) SourceFiles/boxes/autolockbox.h -o GeneratedFiles/Debug/moc_autolockbox.cpp
 
@@ -498,6 +498,12 @@ GeneratedFiles/Debug/moc_mainwidget.cpp: SourceFiles/mainwidget.h
 GeneratedFiles/Debug/moc_mainwindow.cpp: SourceFiles/mainwindow.h
 	$(MOC_FILE) SourceFiles/mainwindow.h -o GeneratedFiles/Debug/moc_mainwindow.cpp
 
+GeneratedFiles/Debug/moc_media_audio.cpp: SourceFiles/media/media_audio.h
+	$(MOC_FILE) SourceFiles/media/media_audio.h -o GeneratedFiles/Debug/moc_media_audio.cpp
+
+GeneratedFiles/Debug/moc_media_clip_reader.cpp: SourceFiles/media/media_clip_reader.h
+	$(MOC_FILE) SourceFiles/media/media_clip_reader.h -o GeneratedFiles/Debug/moc_media_clip_reader.cpp
+
 GeneratedFiles/Debug/moc_mediaview.cpp: SourceFiles/mediaview.h
 	$(MOC_FILE) SourceFiles/mediaview.h -o GeneratedFiles/Debug/moc_mediaview.cpp