Moved audio to media/media_audio and divided to several modules.

Basic video playback with sound support in mediaview added.
This commit is contained in:
John Preston 2016-07-05 20:44:02 +03:00
parent 98fe307cbf
commit 616d08255c
31 changed files with 1684 additions and 878 deletions

View File

@ -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"

View File

@ -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

View File

@ -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;
}

View File

@ -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"

View File

@ -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"

View File

@ -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 = {

View File

@ -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"

View File

@ -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);
}
}
}
}

View File

@ -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()) {

View File

@ -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();

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;
}
}
}

View File

@ -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);
};

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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);

View File

@ -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

View File

@ -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 {

View File

@ -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))

View File

@ -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

View File

@ -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 {

View File

@ -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()) {

View File

@ -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;
};

View File

@ -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 \

View File

@ -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>

View File

@ -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">

View File

@ -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 */,

View File

@ -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