diff --git a/Telegram/SourceFiles/media/media_audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp index 52d424d401..ee60c59f68 100644 --- a/Telegram/SourceFiles/media/media_audio.cpp +++ b/Telegram/SourceFiles/media/media_audio.cpp @@ -636,8 +636,14 @@ void AudioPlayer::stop(AudioMsgId::Type type) { AudioMsgId current; { QMutexLocker lock(&playerMutex); - current = dataForType(type)->audio; + auto data = dataForType(type); + t_assert(data != nullptr); + + current = data->audio; fadedStop(type); + if (type == AudioMsgId::Type::Video) { + data->clear(); + } } if (current) emit updated(current); } diff --git a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp index ab7a141562..4fed50ca00 100644 --- a/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/media_audio_ffmpeg_loader.cpp @@ -235,8 +235,9 @@ AudioPlayerLoader::ReadResult FFMpegLoader::readMore(QByteArray &result, int64 & 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; } - return ReadResult::Error; + return ReadResult::EndOfFile; } if (avpkt.stream_index == streamId) { av_frame_unref(frame); diff --git a/Telegram/SourceFiles/media/media_audio_loader.h b/Telegram/SourceFiles/media/media_audio_loader.h index 42d15a8e19..74a749043d 100644 --- a/Telegram/SourceFiles/media/media_audio_loader.h +++ b/Telegram/SourceFiles/media/media_audio_loader.h @@ -37,6 +37,7 @@ public: NotYet, Ok, Wait, + EndOfFile, }; virtual ReadResult readMore(QByteArray &samples, int64 &samplesCount) = 0; diff --git a/Telegram/SourceFiles/media/media_audio_loaders.cpp b/Telegram/SourceFiles/media/media_audio_loaders.cpp index c18e2e6e82..96b32b2de2 100644 --- a/Telegram/SourceFiles/media/media_audio_loaders.cpp +++ b/Telegram/SourceFiles/media/media_audio_loaders.cpp @@ -30,14 +30,14 @@ AudioPlayerLoaders::AudioPlayerLoaders(QThread *thread) : _fromVideoNotify(this, } void AudioPlayerLoaders::feedFromVideo(VideoSoundPart &&part) { - bool invoke = true; + bool invoke = false; { QMutexLocker lock(&_fromVideoMutex); if (_fromVideoPlayId == part.videoPlayId) { _fromVideoQueue.enqueue(*part.packet); + invoke = true; } else { - av_packet_unref(part.packet); - invoke = false; + FFMpeg::freePacket(part.packet); } } if (invoke) { @@ -77,7 +77,7 @@ AudioPlayerLoaders::~AudioPlayerLoaders() { void AudioPlayerLoaders::clearFromVideoQueue() { auto queue = createAndSwap(_fromVideoQueue); for (auto &packet : queue) { - av_packet_unref(&packet); + FFMpeg::freePacket(&packet); } } @@ -124,7 +124,7 @@ void AudioPlayerLoaders::onLoad(const AudioMsgId &audio) { loadData(audio, 0); } -void AudioPlayerLoaders::loadData(const AudioMsgId &audio, qint64 position) { +void AudioPlayerLoaders::loadData(AudioMsgId audio, qint64 position) { SetupError err = SetupNoErrorStarted; auto type = audio.type(); AudioPlayerLoader *l = setupLoader(audio, err, position); @@ -160,10 +160,13 @@ void AudioPlayerLoaders::loadData(const AudioMsgId &audio, qint64 position) { } finished = true; break; + } else if (res == Result::EndOfFile) { + finished = true; + break; } else if (res == Result::Ok) { errAtStart = false; } else if (res == Result::Wait) { - waiting = samples.isEmpty();// (samples.size() < AudioVoiceMsgBufferSize); + waiting = (samples.size() < AudioVoiceMsgBufferSize); if (waiting) { l->saveDecodedSamples(&samples, &samplesCount); } @@ -335,11 +338,12 @@ AudioPlayerLoader *AudioPlayerLoaders::setupLoader(const AudioMsgId &audio, Setu 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; + case AudioMsgId::Type::Video: l = _videoLoader.get(); isGoodId = (_video == audio); break; } if (l && (!isGoodId || !l->check(data->file, data->data))) { clear(audio.type()); + l = nullptr; } if (!l) { @@ -351,6 +355,12 @@ AudioPlayerLoader *AudioPlayerLoaders::setupLoader(const AudioMsgId &audio, Setu } if (audio.type() == AudioMsgId::Type::Video) { + if (!data->videoData) { + data->state = AudioPlayerStoppedAtError; + emit error(audio); + LOG(("Audio Error: video sound data not ready")); + return nullptr; + } _videoLoader = std_::make_unique(std_::move(data->videoData)); l = _videoLoader.get(); } else { diff --git a/Telegram/SourceFiles/media/media_audio_loaders.h b/Telegram/SourceFiles/media/media_audio_loaders.h index f1893885aa..1aaebd514e 100644 --- a/Telegram/SourceFiles/media/media_audio_loaders.h +++ b/Telegram/SourceFiles/media/media_audio_loaders.h @@ -78,7 +78,7 @@ private: SetupErrorLoadedFull = 2, SetupNoErrorStarted = 3, }; - void loadData(const AudioMsgId &audio, qint64 position); + void loadData(AudioMsgId audio, qint64 position); AudioPlayerLoader *setupLoader(const AudioMsgId &audio, SetupError &err, qint64 position); AudioPlayer::AudioMsg *checkLoader(AudioMsgId::Type type); diff --git a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp index 1f122002e0..f7688934df 100644 --- a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp +++ b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.cpp @@ -113,18 +113,23 @@ bool ChildFFMpegLoader::open(qint64 position) { AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, int64 &samplesAdded) { if (_queue.isEmpty()) { - return ReadResult::Wait; + return _eofReached ? ReadResult::EndOfFile : ReadResult::Wait; } av_frame_unref(_frame); int got_frame = 0; int res = 0; auto packet = _queue.dequeue(); + _eofReached = FFMpeg::isNullPacket(packet); + if (_eofReached) { + return ReadResult::EndOfFile; + } + 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); + FFMpeg::freePacket(&packet); if (res == AVERROR_INVALIDDATA) { return ReadResult::NotYet; // try to skip bad packet } @@ -143,7 +148,7 @@ AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, in 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); + FFMpeg::freePacket(&packet); return ReadResult::Error; } } @@ -151,7 +156,7 @@ AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, in 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); + FFMpeg::freePacket(&packet); return ReadResult::Error; } int32 resultLen = av_samples_get_buffer_size(0, AudioToChannels, res, AudioToFormat, 1); @@ -162,7 +167,7 @@ AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, in samplesAdded += _frame->nb_samples; } } - av_packet_unref(&packet); + FFMpeg::freePacket(&packet); return ReadResult::Ok; } @@ -174,7 +179,7 @@ void ChildFFMpegLoader::enqueuePackets(QQueue &packets) { ChildFFMpegLoader::~ChildFFMpegLoader() { auto queue = createAndSwap(_queue); for (auto &packet : queue) { - av_packet_unref(&packet); + FFMpeg::freePacket(&packet); } if (_dstSamplesData) { if (_dstSamplesData[0]) { diff --git a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h index fff2058c93..f5873472d2 100644 --- a/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h +++ b/Telegram/SourceFiles/media/media_child_ffmpeg_loader.h @@ -44,6 +44,24 @@ struct VideoSoundPart { uint64 videoPlayId = 0; }; +namespace FFMpeg { + +inline bool isNullPacket(const AVPacket &packet) { + return packet.data == nullptr && packet.size == 0; +} + +inline bool isNullPacket(const AVPacket *packet) { + return isNullPacket(*packet); +} + +inline void freePacket(AVPacket *packet) { + if (!isNullPacket(packet)) { + av_packet_unref(packet); + } +} + +} // namespace FFMpeg + class ChildFFMpegLoader : public AudioPlayerLoader { public: ChildFFMpegLoader(std_::unique_ptr &&data); @@ -72,10 +90,15 @@ public: uint64 playId() const { return _parentData->videoPlayId; } + bool eofReached() const { + return _eofReached; + } ~ChildFFMpegLoader(); private: + bool _eofReached = false; + int32 _sampleSize = 2 * sizeof(uint16); int32 _format = AL_FORMAT_STEREO16; int32 _srcRate = AudioVoiceMsgFrequency; diff --git a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp index cd20fd6d8a..55e2cd1e23 100644 --- a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp +++ b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp @@ -107,12 +107,11 @@ bool FFMpegReaderImplementation::readNextFrame() { } if (eofReached) { + clearPacketQueue(); if (_mode == Mode::Normal) { return false; } - clearPacketQueue(); - if ((res = avformat_seek_file(_fmtContext, _streamId, std::numeric_limits::min(), 0, std::numeric_limits::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) { @@ -294,6 +293,9 @@ int FFMpegReaderImplementation::duration() const { } FFMpegReaderImplementation::~FFMpegReaderImplementation() { + if (_mode == Mode::Normal && _audioStreamId >= 0) { + audioPlayer()->stop(AudioMsgId::Type::Video); + } if (_frameRead) { av_frame_unref(_frame); _frameRead = false; @@ -321,6 +323,13 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket( int res = 0; if ((res = av_read_frame(_fmtContext, &packet)) < 0) { if (res == AVERROR_EOF) { + if (_audioStreamId >= 0) { + // queue terminating packet to audio player + VideoSoundPart part; + part.packet = &_packetNull; + part.videoPlayId = _playId; + audioPlayer()->feedFromVideo(std_::move(part)); + } return PacketResult::EndOfFile; } char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; @@ -335,17 +344,8 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket( 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) { - // char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - // LOG(("Gif Error: Unable to av_packet_ref() %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); - // return PacketResult::Error; - //} - if (videoPacket) { _packetQueue.enqueue(packet); - //_packetQueue.enqueue(packetForQueue); } else if (audioPacket) { // queue packet to audio player VideoSoundPart part; @@ -356,7 +356,6 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket( } else { av_packet_unref(&packet); } - //av_packet_unref(&packet); return PacketResult::Ok; } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 1f5a9d073d..834da50ce5 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -222,14 +222,14 @@ bool MediaView::gifShown() const { _gif->start(_gif->width(), _gif->height(), _gif->width(), _gif->height(), false); const_cast(this)->_current = QPixmap(); } - return _gif->state() != Media::Clip::State::Error; + return true;// _gif->state() != Media::Clip::State::Error; } return false; } void MediaView::stopGif() { delete _gif; - _gif = 0; + _gif = nullptr; } void MediaView::documentUpdated(DocumentData *doc) { @@ -506,8 +506,7 @@ void MediaView::clearData() { _a_state.stop(); } if (!_animOpacities.isEmpty()) _animOpacities.clear(); - delete _gif; - _gif = nullptr; + stopGif(); delete _menu; _menu = nullptr; _history = _migrated = nullptr;