1
0
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:
tux3 2015-10-24 04:26:29 +02:00
parent 8ebf07762f
commit c0d8703368
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
10 changed files with 77 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -55,6 +55,7 @@ protected:
private slots:
void onNewFrameAvailable(std::shared_ptr<VideoFrame> newFrame);
void onSourceStopped();
private:
void recalulateBounds();

View File

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