From 1b06fe1220884539d4ad51184f13b909143c3a19 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 30 May 2015 19:30:47 +0300 Subject: [PATCH] ffmpeg audio play / capture done in os x --- Telegram/SourceFiles/audio.cpp | 124 +++++++++++------- Telegram/SourceFiles/audio.h | 8 +- Telegram/SourceFiles/historywidget.cpp | 1 + Telegram/SourceFiles/localimageloader.h | 2 +- Telegram/Telegram.xcodeproj/project.pbxproj | 10 ++ Telegram/Telegram.xcodeproj/qt_preprocess.mak | 1 + 6 files changed, 98 insertions(+), 48 deletions(-) diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp index 618ef4e02e..04abaf8819 100644 --- a/Telegram/SourceFiles/audio.cpp +++ b/Telegram/SourceFiles/audio.cpp @@ -30,6 +30,30 @@ extern "C" { } +#ifdef Q_OS_MAC + +extern "C" { + +#include + +#undef iconv_open +#undef iconv +#undef iconv_close + +iconv_t iconv_open (const char* tocode, const char* fromcode) { + return libiconv_open(tocode, fromcode); +} +size_t iconv (iconv_t cd, char* * inbuf, size_t *inbytesleft, char* * outbuf, size_t *outbytesleft) { + return libiconv(cd, inbuf, inbytesleft, outbuf, outbytesleft); +} +int iconv_close (iconv_t cd) { + return libiconv_close(cd); +} + +} + +#endif + namespace { ALCdevice *audioDevice = 0; ALCcontext *audioContext = 0; @@ -285,48 +309,53 @@ bool AudioPlayer::updateCurrentStarted(int32 pos) { } void AudioPlayer::play(AudioData *audio) { - QMutexLocker lock(&playerMutex); + AudioData *stopped = 0; - bool startNow = true; - if (_data[_current].audio != audio) { - switch (_data[_current].state) { - case AudioPlayerStarting: - case AudioPlayerResuming: - case AudioPlayerPlaying: - _data[_current].state = AudioPlayerFinishing; - updateCurrentStarted(); - startNow = false; - break; - case AudioPlayerPausing: _data[_current].state = AudioPlayerFinishing; startNow = false; break; - case AudioPlayerPaused: _data[_current].state = AudioPlayerStopped; break; - } - if (_data[_current].audio) { - emit loaderOnCancel(_data[_current].audio); - emit faderOnTimer(); - } - } + { + QMutexLocker lock(&playerMutex); - int32 index = 0; - for (; index < AudioVoiceMsgSimultaneously; ++index) { - if (_data[index].audio == audio) { - _current = index; - break; + bool startNow = true; + if (_data[_current].audio != audio) { + switch (_data[_current].state) { + case AudioPlayerStarting: + case AudioPlayerResuming: + case AudioPlayerPlaying: + _data[_current].state = AudioPlayerFinishing; + updateCurrentStarted(); + startNow = false; + break; + case AudioPlayerPausing: _data[_current].state = AudioPlayerFinishing; startNow = false; break; + case AudioPlayerPaused: _data[_current].state = AudioPlayerStopped; stopped = _data[_current].audio; break; + } + if (_data[_current].audio) { + emit loaderOnCancel(_data[_current].audio); + emit faderOnTimer(); + } + } + + int32 index = 0; + for (; index < AudioVoiceMsgSimultaneously; ++index) { + if (_data[index].audio == audio) { + _current = index; + break; + } + } + if (index == AudioVoiceMsgSimultaneously && ++_current >= AudioVoiceMsgSimultaneously) { + _current -= AudioVoiceMsgSimultaneously; + } + _data[_current].audio = audio; + _data[_current].fname = audio->already(true); + _data[_current].data = audio->data; + if (_data[_current].fname.isEmpty() && _data[_current].data.isEmpty()) { + _data[_current].state = AudioPlayerStopped; + onError(audio); + } else if (updateCurrentStarted(0)) { + _data[_current].state = startNow ? AudioPlayerPlaying : AudioPlayerStarting; + _data[_current].loading = true; + emit loaderOnStart(audio); } } - if (index == AudioVoiceMsgSimultaneously && ++_current >= AudioVoiceMsgSimultaneously) { - _current -= AudioVoiceMsgSimultaneously; - } - _data[_current].audio = audio; - _data[_current].fname = audio->already(true); - _data[_current].data = audio->data; - if (_data[_current].fname.isEmpty() && _data[_current].data.isEmpty()) { - _data[_current].state = AudioPlayerStopped; - onError(audio); - } else if (updateCurrentStarted(0)) { - _data[_current].state = startNow ? AudioPlayerPlaying : AudioPlayerStarting; - _data[_current].loading = true; - emit loaderOnStart(audio); - } + if (stopped) emit updated(stopped); } void AudioPlayer::pauseresume() { @@ -491,7 +520,7 @@ void AudioPlayerFader::onTimer() { emit audioStopped(m.audio); } } - if (pos + m.skipStart - m.position >= AudioCheckPositionDelta) { + if (state == AL_PLAYING && pos + m.skipStart - m.position >= AudioCheckPositionDelta) { m.position = pos + m.skipStart; emit playPositionUpdated(m.audio); } @@ -739,7 +768,7 @@ public: return false; } if (avpkt.stream_index == streamId) { - avcodec_get_frame_defaults(frame); + 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 }; @@ -800,7 +829,7 @@ public: private: - int32 freq, fmt, channels; + int32 freq, fmt; int32 sampleSize, srcRate, dstRate, maxResampleSamples; uint8_t **dstSamplesData; int64 len; @@ -1054,6 +1083,8 @@ void AudioPlayerLoaders::onLoad(AudioData *audio) { if (finished) { m.skipEnd = 0; m.duration = m.skipStart + m.samplesCount[0] + m.samplesCount[1] + m.samplesCount[2]; + delete j.value(); + _loaders.erase(j); } m.loading = false; if (m.state == AudioPlayerResuming || m.state == AudioPlayerPlaying || m.state == AudioPlayerStarting) { @@ -1172,7 +1203,7 @@ void AudioCaptureInner::onStart() { // Start OpenAL Capture - d->device = alcCaptureOpenDevice(0, AudioVoiceMsgFrequency, AL_FORMAT_MONO16, AudioVoiceMsgBufferSize); + d->device = alcCaptureOpenDevice(0, AudioVoiceMsgFrequency, AL_FORMAT_MONO16, AudioVoiceMsgFrequency / 5); if (!d->device) { LOG(("Audio Error: capture device not present!")); emit error(); @@ -1457,7 +1488,7 @@ void AudioCaptureInner::onTimeout() { } // Write frames int32 framesize = d->srcSamples * d->codecContext->channels * sizeof(short), encoded = 0; - while (_captured.size() >= encoded + framesize + fadeSamples * sizeof(short)) { + while (uint32(_captured.size()) >= encoded + framesize + fadeSamples * sizeof(short)) { writeFrame(encoded, framesize); encoded += framesize; } @@ -1527,8 +1558,9 @@ void AudioCaptureInner::writeFrame(int32 offset, int32 framesize) { // Write audio frame - AVPacket pkt = { 0 }; // data and size must be 0; - AVFrame *frame = avcodec_alloc_frame(); + AVPacket pkt; + memset(&pkt, 0, sizeof(pkt)); // data and size must be 0; + AVFrame *frame = av_frame_alloc(); int gotPacket; av_init_packet(&pkt); @@ -1552,5 +1584,5 @@ void AudioCaptureInner::writeFrame(int32 offset, int32 framesize) { } d->fullSamples += samplesCnt; - avcodec_free_frame(&frame); + av_frame_free(&frame); } \ No newline at end of file diff --git a/Telegram/SourceFiles/audio.h b/Telegram/SourceFiles/audio.h index 8a328e4e6f..6216366add 100644 --- a/Telegram/SourceFiles/audio.h +++ b/Telegram/SourceFiles/audio.h @@ -35,6 +35,9 @@ enum AudioPlayerState { AudioPlayerResuming, }; +class AudioPlayerFader; +class AudioPlayerLoaders; + class AudioPlayer : public QObject { Q_OBJECT @@ -104,6 +107,8 @@ private: }; +class AudioCaptureInner; + class AudioCapture : public QObject { Q_OBJECT @@ -199,6 +204,8 @@ private: }; +struct AudioCapturePrivate; + class AudioCaptureInner : public QObject { Q_OBJECT @@ -225,7 +232,6 @@ private: void writeFrame(int32 offset, int32 framesize); - friend struct AudioCapturePrivate; AudioCapturePrivate *d; QTimer _timer; QByteArray _captured; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 35b7991991..fa4b9a53fc 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3031,6 +3031,7 @@ void HistoryWidget::stopRecording(bool send) { _recordingAnim.stop(); _recording = false; + _recordingSamples = 0; updateControlsVisibility(); activate(); diff --git a/Telegram/SourceFiles/localimageloader.h b/Telegram/SourceFiles/localimageloader.h index cab46a0336..a3850142a5 100644 --- a/Telegram/SourceFiles/localimageloader.h +++ b/Telegram/SourceFiles/localimageloader.h @@ -49,7 +49,7 @@ typedef QList ToPrepareMedias; typedef QMap LocalFileParts; struct ReadyLocalMedia { ReadyLocalMedia(ToPrepareMediaType type, const QString &file, const QString &filename, int32 filesize, const QByteArray &data, const uint64 &id, const uint64 &thumbId, const QString &thumbExt, const PeerId &peer, const MTPPhoto &photo, const MTPAudio &audio, const PreparedPhotoThumbs &photoThumbs, const MTPDocument &document, const QByteArray &jpeg, bool ctrlShiftEnter, MsgId replyTo) : - replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), photoThumbs(photoThumbs), audio(audio), ctrlShiftEnter(ctrlShiftEnter) { + replyTo(replyTo), type(type), file(file), filename(filename), filesize(filesize), data(data), thumbExt(thumbExt), id(id), thumbId(thumbId), peer(peer), photo(photo), document(document), audio(audio), photoThumbs(photoThumbs), ctrlShiftEnter(ctrlShiftEnter) { if (!jpeg.isEmpty()) { int32 size = jpeg.size(); for (int32 i = 0, part = 0; i < size; i += UploadPartSize, ++part) { diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 45b29e6dc5..e35874bb75 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1863,6 +1863,11 @@ /usr/local/lib/libmpg123.a, /usr/local/lib/libfaad.a, /usr/local/lib/libmp4ff.a, + /usr/local/lib/libavcodec.a, + /usr/local/lib/libavformat.a, + /usr/local/lib/libswresample.a, + /usr/local/lib/libavutil.a, + /usr/local/lib/libiconv.a, "../../Libraries/openssl-xcode/libcrypto.a", ); PRODUCT_NAME = Telegram; @@ -2010,6 +2015,11 @@ /usr/local/lib/libmpg123.a, /usr/local/lib/libfaad.a, /usr/local/lib/libmp4ff.a, + /usr/local/lib/libavcodec.a, + /usr/local/lib/libavformat.a, + /usr/local/lib/libswresample.a, + /usr/local/lib/libavutil.a, + /usr/local/lib/libiconv.a, "../../Libraries/openssl-xcode/libcrypto.a", ); PRODUCT_NAME = Telegram; diff --git a/Telegram/Telegram.xcodeproj/qt_preprocess.mak b/Telegram/Telegram.xcodeproj/qt_preprocess.mak index a5b9098681..f69f9bc47b 100644 --- a/Telegram/Telegram.xcodeproj/qt_preprocess.mak +++ b/Telegram/Telegram.xcodeproj/qt_preprocess.mak @@ -131,6 +131,7 @@ GeneratedFiles/Debug/moc_application.cpp: ../../Libraries/QtStatic/qtbase/includ /usr/local/Qt-5.4.0/bin/moc $(DEFINES) -D__APPLE__ -D__GNUC__=4 -I/usr/local/Qt-5.4.0/mkspecs/macx-clang -I. -I/usr/local/Qt-5.4.0/include/QtGui/5.4.0/QtGui -I/usr/local/Qt-5.4.0/include/QtCore/5.4.0/QtCore -I/usr/local/Qt-5.4.0/include -I./SourceFiles -I./GeneratedFiles -I../../Libraries/lzma/C -I../../Libraries/libexif-0.6.20 -I/usr/local/Qt-5.4.0/include -I/usr/local/Qt-5.4.0/include/QtMultimedia -I/usr/local/Qt-5.4.0/include/QtWidgets -I/usr/local/Qt-5.4.0/include/QtNetwork -I/usr/local/Qt-5.4.0/include/QtGui -I/usr/local/Qt-5.4.0/include/QtCore -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1/backward -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.1/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include SourceFiles/application.h -o GeneratedFiles/Debug/moc_application.cpp GeneratedFiles/Debug/moc_audio.cpp: SourceFiles/types.h \ + SourceFiles/audio.h \ ../../Libraries/QtStatic/qtbase/include/QtCore/QReadWriteLock \ SourceFiles/logs.h \ ../../Libraries/QtStatic/qtbase/include/QtCore/QTimer \