mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Properly signal when we stop sending video
And properly handle toxav happily delivering things out of order, like firing a video frame callback right after a callback setting the bitrate to 0, when the peer sent these commands in the right order
This commit is contained in:
parent
8ebf07762f
commit
c0d8703368
|
@ -260,6 +260,13 @@ void CoreAV::sendCallVideo(uint32_t callId, shared_ptr<VideoFrame> vframe)
|
|||
|| !(call.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V))
|
||||
return;
|
||||
|
||||
if (call.nullVideoBitrate)
|
||||
{
|
||||
qDebug() << "Restarting video stream to friend"<<callId;
|
||||
toxav_bit_rate_set(toxav, call.callId, -1, VIDEO_DEFAULT_BITRATE, nullptr);
|
||||
call.nullVideoBitrate = false;
|
||||
}
|
||||
|
||||
// This frame shares vframe's buffers, we don't call vpx_img_free but just delete it
|
||||
vpx_image* frame = vframe->toVpxImage();
|
||||
if (frame->fmt == VPX_IMG_FMT_NONE)
|
||||
|
@ -406,6 +413,17 @@ void CoreAV::resetCallSources()
|
|||
}
|
||||
}
|
||||
|
||||
void CoreAV::sendNoVideo()
|
||||
{
|
||||
// We don't change the audio bitrate, but we signal that we're not sending video anymore
|
||||
qDebug() << "CoreAV: Signaling end of video sending";
|
||||
for (ToxFriendCall& call : calls)
|
||||
{
|
||||
toxav_bit_rate_set(toxav, call.callId, -1, 0, nullptr);
|
||||
call.nullVideoBitrate = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool video, void *_self)
|
||||
{
|
||||
CoreAV* self = static_cast<CoreAV*>(_self);
|
||||
|
@ -481,6 +499,20 @@ void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, voi
|
|||
call.inactive = false;
|
||||
emit self->avStart(friendNum, call.videoEnabled);
|
||||
}
|
||||
else if ((call.state & TOXAV_FRIEND_CALL_STATE_SENDING_V)
|
||||
&& !(state & TOXAV_FRIEND_CALL_STATE_SENDING_V))
|
||||
{
|
||||
qDebug() << "Friend"<<friendNum<<"stopped sending video";
|
||||
call.videoSource->stopSource();
|
||||
}
|
||||
else if (!(call.state & TOXAV_FRIEND_CALL_STATE_SENDING_V)
|
||||
&& (state & TOXAV_FRIEND_CALL_STATE_SENDING_V))
|
||||
{
|
||||
// Workaround toxav sometimes firing callbacks for "send last frame" -> "stop sending video"
|
||||
// out of orders (even though they were sent in order by the other end).
|
||||
// We simply stop the videoSource from emitting anything while the other end says it's not sending
|
||||
call.videoSource->restartSource();
|
||||
}
|
||||
|
||||
call.state = static_cast<TOXAV_FRIEND_CALL_STATE>(state);
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ public:
|
|||
|
||||
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
||||
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
||||
void sendNoVideo(); ///< Signal to all peers that we're not sending video anymore. The next frame sent cancels this.
|
||||
|
||||
void joinGroupCall(int groupNum); ///< Starts a call in an existing AV groupchat. Call from the GUI thread.
|
||||
void leaveGroupCall(int groupNum); ///< Will not leave the group, just stop the call. Call from the GUI thread.
|
||||
|
|
|
@ -117,7 +117,7 @@ void ToxFriendCall::stopTimeout()
|
|||
|
||||
ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
||||
: ToxCall(FriendNum),
|
||||
videoEnabled{VideoEnabled}, videoSource{nullptr},
|
||||
videoEnabled{VideoEnabled}, nullVideoBitrate{false}, videoSource{nullptr},
|
||||
state{static_cast<TOXAV_FRIEND_CALL_STATE>(0)},
|
||||
av{&av}, timeoutTimer{nullptr}
|
||||
{
|
||||
|
@ -144,8 +144,9 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
|
|||
|
||||
ToxFriendCall::ToxFriendCall(ToxFriendCall&& other)
|
||||
: ToxCall(move(other)),
|
||||
videoEnabled{other.videoEnabled}, videoSource{other.videoSource},
|
||||
state{other.state}, av{other.av}, timeoutTimer{other.timeoutTimer}
|
||||
videoEnabled{other.videoEnabled}, nullVideoBitrate{other.nullVideoBitrate},
|
||||
videoSource{other.videoSource}, state{other.state},
|
||||
av{other.av}, timeoutTimer{other.timeoutTimer}
|
||||
{
|
||||
other.videoEnabled = false;
|
||||
other.videoSource = nullptr;
|
||||
|
@ -182,6 +183,7 @@ const ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall&& other)
|
|||
timeoutTimer = other.timeoutTimer;
|
||||
other.timeoutTimer = nullptr;
|
||||
av = other.av;
|
||||
nullVideoBitrate = other.nullVideoBitrate;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ struct ToxFriendCall : public ToxCall
|
|||
const ToxFriendCall& operator=(ToxFriendCall&& other) noexcept;
|
||||
|
||||
bool videoEnabled; ///< True if our user asked for a video call, sending and recving
|
||||
bool nullVideoBitrate; ///< True if our video bitrate is zero, i.e. if the device is closed
|
||||
CoreVideoSource* videoSource;
|
||||
TOXAV_FRIEND_CALL_STATE state; ///< State of the peer (not ours!)
|
||||
|
||||
|
|
|
@ -26,12 +26,15 @@ extern "C" {
|
|||
|
||||
CoreVideoSource::CoreVideoSource()
|
||||
: subscribers{0}, deleteOnClose{false},
|
||||
biglock{false}
|
||||
biglock{false}, stopped{false}
|
||||
{
|
||||
}
|
||||
|
||||
void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe)
|
||||
{
|
||||
if (stopped)
|
||||
return;
|
||||
|
||||
// Fast lock
|
||||
{
|
||||
bool expected = false;
|
||||
|
@ -126,3 +129,14 @@ void CoreVideoSource::setDeleteOnClose(bool newstate)
|
|||
deleteOnClose = newstate;
|
||||
biglock = false;
|
||||
}
|
||||
|
||||
void CoreVideoSource::stopSource()
|
||||
{
|
||||
stopped = true;
|
||||
emit sourceStopped();
|
||||
}
|
||||
|
||||
void CoreVideoSource::restartSource()
|
||||
{
|
||||
stopped = false;
|
||||
}
|
||||
|
|
|
@ -44,10 +44,16 @@ private:
|
|||
/// If true, self-delete after the last suscriber is gone
|
||||
void setDeleteOnClose(bool newstate);
|
||||
|
||||
/// Stopping the source will block any pushFrame calls from doing anything
|
||||
/// See the callers in CoreAV for the rationale
|
||||
void stopSource();
|
||||
void restartSource();
|
||||
|
||||
private:
|
||||
std::atomic_int subscribers; ///< Number of suscribers
|
||||
std::atomic_bool deleteOnClose; ///< If true, self-delete after the last suscriber is gone
|
||||
std::atomic_bool biglock; ///< Fast lock
|
||||
std::atomic_bool stopped;
|
||||
|
||||
friend class CoreAV;
|
||||
friend struct ToxFriendCall;
|
||||
|
|
|
@ -41,6 +41,9 @@ public:
|
|||
|
||||
signals:
|
||||
void frameAvailable(std::shared_ptr<VideoFrame> frame);
|
||||
/// Emitted when the source is stopped for an indefinite amount of time,
|
||||
/// but might restart sending frames again later
|
||||
void sourceStopped();
|
||||
};
|
||||
|
||||
#endif // VIDEOSOURCE_H
|
||||
|
|
|
@ -102,6 +102,7 @@ void VideoSurface::subscribe()
|
|||
{
|
||||
source->subscribe();
|
||||
connect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable);
|
||||
connect(source, &VideoSource::sourceStopped, this, &VideoSurface::onSourceStopped);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,6 +125,7 @@ void VideoSurface::unsubscribe()
|
|||
|
||||
source->unsubscribe();
|
||||
disconnect(source, &VideoSource::frameAvailable, this, &VideoSurface::onNewFrameAvailable);
|
||||
disconnect(source, &VideoSource::sourceStopped, this, &VideoSurface::onSourceStopped);
|
||||
}
|
||||
|
||||
void VideoSurface::onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame)
|
||||
|
@ -148,6 +150,13 @@ void VideoSurface::onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame)
|
|||
update();
|
||||
}
|
||||
|
||||
void VideoSurface::onSourceStopped()
|
||||
{
|
||||
// If the source's stream is on hold, just revert back to the avatar view
|
||||
lastFrame.reset();
|
||||
update();
|
||||
}
|
||||
|
||||
void VideoSurface::paintEvent(QPaintEvent*)
|
||||
{
|
||||
lock();
|
||||
|
|
|
@ -55,6 +55,7 @@ protected:
|
|||
|
||||
private slots:
|
||||
void onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame);
|
||||
void onSourceStopped();
|
||||
|
||||
private:
|
||||
void recalulateBounds();
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "src/video/cameradevice.h"
|
||||
#include "src/video/videosurface.h"
|
||||
#include "src/widget/translator.h"
|
||||
#include "src/core/core.h"
|
||||
#include "src/core/coreav.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <OpenAL/al.h>
|
||||
|
@ -210,6 +212,8 @@ void AVForm::onVideoDevChanged(int index)
|
|||
camera.open(dev);
|
||||
killVideoSurface();
|
||||
createVideoSurface();
|
||||
if (dev == "none")
|
||||
Core::getInstance()->getAv()->sendNoVideo();
|
||||
}
|
||||
|
||||
void AVForm::hideEvent(QHideEvent *)
|
||||
|
|
Loading…
Reference in New Issue
Block a user