Stopping video sound when closing mediaview (destroying Clip::Reader).

Fixed launching video with sound while song is playing.
This commit is contained in:
John Preston 2016-07-05 20:44:22 +03:00
parent 616d08255c
commit 99b15719cf
9 changed files with 76 additions and 32 deletions

View File

@ -636,8 +636,14 @@ void AudioPlayer::stop(AudioMsgId::Type type) {
AudioMsgId current; AudioMsgId current;
{ {
QMutexLocker lock(&playerMutex); QMutexLocker lock(&playerMutex);
current = dataForType(type)->audio; auto data = dataForType(type);
t_assert(data != nullptr);
current = data->audio;
fadedStop(type); fadedStop(type);
if (type == AudioMsgId::Type::Video) {
data->clear();
}
} }
if (current) emit updated(current); if (current) emit updated(current);
} }

View File

@ -235,8 +235,9 @@ AudioPlayerLoader::ReadResult FFMpegLoader::readMore(QByteArray &result, int64 &
if (res != AVERROR_EOF) { if (res != AVERROR_EOF) {
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; 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))); 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) { if (avpkt.stream_index == streamId) {
av_frame_unref(frame); av_frame_unref(frame);

View File

@ -37,6 +37,7 @@ public:
NotYet, NotYet,
Ok, Ok,
Wait, Wait,
EndOfFile,
}; };
virtual ReadResult readMore(QByteArray &samples, int64 &samplesCount) = 0; virtual ReadResult readMore(QByteArray &samples, int64 &samplesCount) = 0;

View File

@ -30,14 +30,14 @@ AudioPlayerLoaders::AudioPlayerLoaders(QThread *thread) : _fromVideoNotify(this,
} }
void AudioPlayerLoaders::feedFromVideo(VideoSoundPart &&part) { void AudioPlayerLoaders::feedFromVideo(VideoSoundPart &&part) {
bool invoke = true; bool invoke = false;
{ {
QMutexLocker lock(&_fromVideoMutex); QMutexLocker lock(&_fromVideoMutex);
if (_fromVideoPlayId == part.videoPlayId) { if (_fromVideoPlayId == part.videoPlayId) {
_fromVideoQueue.enqueue(*part.packet); _fromVideoQueue.enqueue(*part.packet);
invoke = true;
} else { } else {
av_packet_unref(part.packet); FFMpeg::freePacket(part.packet);
invoke = false;
} }
} }
if (invoke) { if (invoke) {
@ -77,7 +77,7 @@ AudioPlayerLoaders::~AudioPlayerLoaders() {
void AudioPlayerLoaders::clearFromVideoQueue() { void AudioPlayerLoaders::clearFromVideoQueue() {
auto queue = createAndSwap(_fromVideoQueue); auto queue = createAndSwap(_fromVideoQueue);
for (auto &packet : queue) { for (auto &packet : queue) {
av_packet_unref(&packet); FFMpeg::freePacket(&packet);
} }
} }
@ -124,7 +124,7 @@ void AudioPlayerLoaders::onLoad(const AudioMsgId &audio) {
loadData(audio, 0); loadData(audio, 0);
} }
void AudioPlayerLoaders::loadData(const AudioMsgId &audio, qint64 position) { void AudioPlayerLoaders::loadData(AudioMsgId audio, qint64 position) {
SetupError err = SetupNoErrorStarted; SetupError err = SetupNoErrorStarted;
auto type = audio.type(); auto type = audio.type();
AudioPlayerLoader *l = setupLoader(audio, err, position); AudioPlayerLoader *l = setupLoader(audio, err, position);
@ -160,10 +160,13 @@ void AudioPlayerLoaders::loadData(const AudioMsgId &audio, qint64 position) {
} }
finished = true; finished = true;
break; break;
} else if (res == Result::EndOfFile) {
finished = true;
break;
} else if (res == Result::Ok) { } else if (res == Result::Ok) {
errAtStart = false; errAtStart = false;
} else if (res == Result::Wait) { } else if (res == Result::Wait) {
waiting = samples.isEmpty();// (samples.size() < AudioVoiceMsgBufferSize); waiting = (samples.size() < AudioVoiceMsgBufferSize);
if (waiting) { if (waiting) {
l->saveDecodedSamples(&samples, &samplesCount); l->saveDecodedSamples(&samples, &samplesCount);
} }
@ -335,11 +338,12 @@ AudioPlayerLoader *AudioPlayerLoaders::setupLoader(const AudioMsgId &audio, Setu
switch (audio.type()) { switch (audio.type()) {
case AudioMsgId::Type::Voice: l = _audioLoader.get(); isGoodId = (_audio == audio); break; 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::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))) { if (l && (!isGoodId || !l->check(data->file, data->data))) {
clear(audio.type()); clear(audio.type());
l = nullptr;
} }
if (!l) { if (!l) {
@ -351,6 +355,12 @@ AudioPlayerLoader *AudioPlayerLoaders::setupLoader(const AudioMsgId &audio, Setu
} }
if (audio.type() == AudioMsgId::Type::Video) { 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<ChildFFMpegLoader>(std_::move(data->videoData)); _videoLoader = std_::make_unique<ChildFFMpegLoader>(std_::move(data->videoData));
l = _videoLoader.get(); l = _videoLoader.get();
} else { } else {

View File

@ -78,7 +78,7 @@ private:
SetupErrorLoadedFull = 2, SetupErrorLoadedFull = 2,
SetupNoErrorStarted = 3, SetupNoErrorStarted = 3,
}; };
void loadData(const AudioMsgId &audio, qint64 position); void loadData(AudioMsgId audio, qint64 position);
AudioPlayerLoader *setupLoader(const AudioMsgId &audio, SetupError &err, qint64 position); AudioPlayerLoader *setupLoader(const AudioMsgId &audio, SetupError &err, qint64 position);
AudioPlayer::AudioMsg *checkLoader(AudioMsgId::Type type); AudioPlayer::AudioMsg *checkLoader(AudioMsgId::Type type);

View File

@ -113,18 +113,23 @@ bool ChildFFMpegLoader::open(qint64 position) {
AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, int64 &samplesAdded) { AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, int64 &samplesAdded) {
if (_queue.isEmpty()) { if (_queue.isEmpty()) {
return ReadResult::Wait; return _eofReached ? ReadResult::EndOfFile : ReadResult::Wait;
} }
av_frame_unref(_frame); av_frame_unref(_frame);
int got_frame = 0; int got_frame = 0;
int res = 0; int res = 0;
auto packet = _queue.dequeue(); 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) { if ((res = avcodec_decode_audio4(_parentData->context, _frame, &got_frame, &packet)) < 0) {
char err[AV_ERROR_MAX_STRING_SIZE] = { 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))); 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) { if (res == AVERROR_INVALIDDATA) {
return ReadResult::NotYet; // try to skip bad packet 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 }; 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))); 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; return ReadResult::Error;
} }
} }
@ -151,7 +156,7 @@ AudioPlayerLoader::ReadResult ChildFFMpegLoader::readMore(QByteArray &result, in
char err[AV_ERROR_MAX_STRING_SIZE] = { 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))); 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; return ReadResult::Error;
} }
int32 resultLen = av_samples_get_buffer_size(0, AudioToChannels, res, AudioToFormat, 1); 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; samplesAdded += _frame->nb_samples;
} }
} }
av_packet_unref(&packet); FFMpeg::freePacket(&packet);
return ReadResult::Ok; return ReadResult::Ok;
} }
@ -174,7 +179,7 @@ void ChildFFMpegLoader::enqueuePackets(QQueue<AVPacket> &packets) {
ChildFFMpegLoader::~ChildFFMpegLoader() { ChildFFMpegLoader::~ChildFFMpegLoader() {
auto queue = createAndSwap(_queue); auto queue = createAndSwap(_queue);
for (auto &packet : queue) { for (auto &packet : queue) {
av_packet_unref(&packet); FFMpeg::freePacket(&packet);
} }
if (_dstSamplesData) { if (_dstSamplesData) {
if (_dstSamplesData[0]) { if (_dstSamplesData[0]) {

View File

@ -44,6 +44,24 @@ struct VideoSoundPart {
uint64 videoPlayId = 0; 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 { class ChildFFMpegLoader : public AudioPlayerLoader {
public: public:
ChildFFMpegLoader(std_::unique_ptr<VideoSoundData> &&data); ChildFFMpegLoader(std_::unique_ptr<VideoSoundData> &&data);
@ -72,10 +90,15 @@ public:
uint64 playId() const { uint64 playId() const {
return _parentData->videoPlayId; return _parentData->videoPlayId;
} }
bool eofReached() const {
return _eofReached;
}
~ChildFFMpegLoader(); ~ChildFFMpegLoader();
private: private:
bool _eofReached = false;
int32 _sampleSize = 2 * sizeof(uint16); int32 _sampleSize = 2 * sizeof(uint16);
int32 _format = AL_FORMAT_STEREO16; int32 _format = AL_FORMAT_STEREO16;
int32 _srcRate = AudioVoiceMsgFrequency; int32 _srcRate = AudioVoiceMsgFrequency;

View File

@ -107,12 +107,11 @@ bool FFMpegReaderImplementation::readNextFrame() {
} }
if (eofReached) { if (eofReached) {
clearPacketQueue();
if (_mode == Mode::Normal) { if (_mode == Mode::Normal) {
return false; 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 = 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_BYTE)) < 0) {
if ((res = av_seek_frame(_fmtContext, _streamId, 0, AVSEEK_FLAG_FRAME)) < 0) { if ((res = av_seek_frame(_fmtContext, _streamId, 0, AVSEEK_FLAG_FRAME)) < 0) {
@ -294,6 +293,9 @@ int FFMpegReaderImplementation::duration() const {
} }
FFMpegReaderImplementation::~FFMpegReaderImplementation() { FFMpegReaderImplementation::~FFMpegReaderImplementation() {
if (_mode == Mode::Normal && _audioStreamId >= 0) {
audioPlayer()->stop(AudioMsgId::Type::Video);
}
if (_frameRead) { if (_frameRead) {
av_frame_unref(_frame); av_frame_unref(_frame);
_frameRead = false; _frameRead = false;
@ -321,6 +323,13 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket(
int res = 0; int res = 0;
if ((res = av_read_frame(_fmtContext, &packet)) < 0) { if ((res = av_read_frame(_fmtContext, &packet)) < 0) {
if (res == AVERROR_EOF) { 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; return PacketResult::EndOfFile;
} }
char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; 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; int64 packetMs = (packetPts * 1000LL * _fmtContext->streams[packet.stream_index]->time_base.num) / _fmtContext->streams[packet.stream_index]->time_base.den;
_lastReadPacketMs = packetMs; _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) { if (videoPacket) {
_packetQueue.enqueue(packet); _packetQueue.enqueue(packet);
//_packetQueue.enqueue(packetForQueue);
} else if (audioPacket) { } else if (audioPacket) {
// queue packet to audio player // queue packet to audio player
VideoSoundPart part; VideoSoundPart part;
@ -356,7 +356,6 @@ FFMpegReaderImplementation::PacketResult FFMpegReaderImplementation::readPacket(
} else { } else {
av_packet_unref(&packet); av_packet_unref(&packet);
} }
//av_packet_unref(&packet);
return PacketResult::Ok; return PacketResult::Ok;
} }

View File

@ -222,14 +222,14 @@ bool MediaView::gifShown() const {
_gif->start(_gif->width(), _gif->height(), _gif->width(), _gif->height(), false); _gif->start(_gif->width(), _gif->height(), _gif->width(), _gif->height(), false);
const_cast<MediaView*>(this)->_current = QPixmap(); const_cast<MediaView*>(this)->_current = QPixmap();
} }
return _gif->state() != Media::Clip::State::Error; return true;// _gif->state() != Media::Clip::State::Error;
} }
return false; return false;
} }
void MediaView::stopGif() { void MediaView::stopGif() {
delete _gif; delete _gif;
_gif = 0; _gif = nullptr;
} }
void MediaView::documentUpdated(DocumentData *doc) { void MediaView::documentUpdated(DocumentData *doc) {
@ -506,8 +506,7 @@ void MediaView::clearData() {
_a_state.stop(); _a_state.stop();
} }
if (!_animOpacities.isEmpty()) _animOpacities.clear(); if (!_animOpacities.isEmpty()) _animOpacities.clear();
delete _gif; stopGif();
_gif = nullptr;
delete _menu; delete _menu;
_menu = nullptr; _menu = nullptr;
_history = _migrated = nullptr; _history = _migrated = nullptr;