From 8ecdd03dd67231aa9a54f128dbb20817aeae68ae Mon Sep 17 00:00:00 2001 From: Raphael Dumusc Date: Tue, 7 Feb 2017 10:52:25 +0100 Subject: [PATCH] CR#1: allow single finishFrame after sending left + right image --- deflect/FrameDispatcher.cpp | 28 ++++++++----------- deflect/FrameDispatcher.h | 4 +-- deflect/ReceiveBuffer.cpp | 19 +++++++++---- deflect/ReceiveBuffer.h | 4 +-- deflect/ServerWorker.cpp | 2 +- deflect/ServerWorker.h | 3 +-- deflect/SourceBuffer.cpp | 5 ++++ deflect/SourceBuffer.h | 3 +++ doc/StereoStreaming.md | 46 ++++++++++++++++++++++++++++---- tests/cpp/ReceiveBufferTests.cpp | 25 ++++++++++++++++- 10 files changed, 102 insertions(+), 37 deletions(-) diff --git a/deflect/FrameDispatcher.cpp b/deflect/FrameDispatcher.cpp index ac73337..207a085 100644 --- a/deflect/FrameDispatcher.cpp +++ b/deflect/FrameDispatcher.cpp @@ -134,8 +134,7 @@ void FrameDispatcher::processSegment( const QString uri, } void FrameDispatcher::processFrameFinished( const QString uri, - const size_t sourceIndex, - const deflect::View view ) + const size_t sourceIndex ) { if( !_impl->streamBuffers.count( uri )) return; @@ -143,7 +142,7 @@ void FrameDispatcher::processFrameFinished( const QString uri, ReceiveBuffer& buffer = _impl->streamBuffers[uri]; try { - buffer.finishFrameForSource( sourceIndex, view ); + buffer.finishFrameForSource( sourceIndex ); } catch( const std::runtime_error& ) { @@ -151,21 +150,16 @@ void FrameDispatcher::processFrameFinished( const QString uri, return; } - if( view == View::MONO ) - { - if( buffer.isAllowedToSend( view ) && buffer.hasCompleteMonoFrame( )) - emit sendFrame( _impl->consumeLatestMonoFrame( uri )); - } - else + if( buffer.isAllowedToSend( View::MONO ) && buffer.hasCompleteMonoFrame( )) + emit sendFrame( _impl->consumeLatestMonoFrame( uri )); + + if( buffer.isAllowedToSend( View::LEFT_EYE ) && + buffer.isAllowedToSend( View::RIGHT_EYE ) && + buffer.hasCompleteStereoFrame( )) { - if( buffer.isAllowedToSend( View::LEFT_EYE ) && - buffer.isAllowedToSend( View::RIGHT_EYE ) && - buffer.hasCompleteStereoFrame( )) - { - const auto frames = _impl->consumeLatestStereoFrame( uri ); - emit sendFrame( frames.first ); - emit sendFrame( frames.second ); - } + const auto frames = _impl->consumeLatestStereoFrame( uri ); + emit sendFrame( frames.first ); + emit sendFrame( frames.second ); } } diff --git a/deflect/FrameDispatcher.h b/deflect/FrameDispatcher.h index 81b0698..caf1f9c 100644 --- a/deflect/FrameDispatcher.h +++ b/deflect/FrameDispatcher.h @@ -96,10 +96,8 @@ public slots: * * @param uri Identifier for the stream * @param sourceIndex Identifier for the source in the stream - * @param view for which the frame is finished */ - void processFrameFinished( QString uri, size_t sourceIndex, - deflect::View view ); + void processFrameFinished( QString uri, size_t sourceIndex ); /** * Request the dispatching of a new frame for any stream (MONO/STEREO). diff --git a/deflect/ReceiveBuffer.cpp b/deflect/ReceiveBuffer.cpp index 5711e14..34261fa 100644 --- a/deflect/ReceiveBuffer.cpp +++ b/deflect/ReceiveBuffer.cpp @@ -39,12 +39,16 @@ #include "ReceiveBuffer.h" +#include #include #include namespace { const size_t MAX_QUEUE_SIZE = 150; // stream blocked for ~5 seconds at 30Hz +const auto views = std::array{{ deflect::View::MONO, + deflect::View::LEFT_EYE, + deflect::View::RIGHT_EYE }}; } namespace deflect @@ -81,17 +85,22 @@ void ReceiveBuffer::insert( const Segment& segment, const size_t sourceIndex, _sourceBuffers[sourceIndex].insert( segment, view ); } -void ReceiveBuffer::finishFrameForSource( const size_t sourceIndex, - const deflect::View view ) +void ReceiveBuffer::finishFrameForSource( const size_t sourceIndex ) { assert( _sourceBuffers.count( sourceIndex )); auto& buffer = _sourceBuffers[sourceIndex]; - if( buffer.getQueueSize( view ) > MAX_QUEUE_SIZE ) - throw std::runtime_error( "maximum queue size exceeded" ); + for( const auto view : views ) + { + if( buffer.isBackFrameEmpty( view )) + continue; + + if( buffer.getQueueSize( view ) > MAX_QUEUE_SIZE ) + throw std::runtime_error( "maximum queue size exceeded" ); - buffer.push( view ); + buffer.push( view ); + } } bool ReceiveBuffer::hasCompleteMonoFrame() const diff --git a/deflect/ReceiveBuffer.h b/deflect/ReceiveBuffer.h index 3b88642..efbfad1 100644 --- a/deflect/ReceiveBuffer.h +++ b/deflect/ReceiveBuffer.h @@ -90,11 +90,9 @@ class ReceiveBuffer /** * Call when the source has finished sending segments for the current frame. * @param sourceIndex Unique source identifier - * @param view for which to finish the frame * @throw std::runtime_error if the buffer exceeds its maximum size */ - DEFLECT_API void finishFrameForSource( - size_t sourceIndex, deflect::View view = deflect::View::MONO ); + DEFLECT_API void finishFrameForSource( size_t sourceIndex ); /** Does the Buffer have a new complete mono frame (from all sources) */ DEFLECT_API bool hasCompleteMonoFrame() const; diff --git a/deflect/ServerWorker.cpp b/deflect/ServerWorker.cpp index 5da1a94..5a09526 100644 --- a/deflect/ServerWorker.cpp +++ b/deflect/ServerWorker.cpp @@ -235,7 +235,7 @@ void ServerWorker::_handleMessage( const MessageHeader& messageHeader, break; case MESSAGE_TYPE_PIXELSTREAM_FINISH_FRAME: - emit receivedFrameFinished( _streamId, _sourceId, _activeView ); + emit receivedFrameFinished( _streamId, _sourceId ); break; case MESSAGE_TYPE_PIXELSTREAM: diff --git a/deflect/ServerWorker.h b/deflect/ServerWorker.h index 1ee8e49..809e8ca 100644 --- a/deflect/ServerWorker.h +++ b/deflect/ServerWorker.h @@ -74,8 +74,7 @@ public slots: void receivedSegment( QString uri, size_t sourceIndex, deflect::Segment segment, deflect::View view ); - void receivedFrameFinished( QString uri, size_t sourceIndex, - deflect::View view ); + void receivedFrameFinished( QString uri, size_t sourceIndex ); void registerToEvents( QString uri, bool exclusive, deflect::EventReceiver* receiver ); diff --git a/deflect/SourceBuffer.cpp b/deflect/SourceBuffer.cpp index ea05ca7..7e89e07 100644 --- a/deflect/SourceBuffer.cpp +++ b/deflect/SourceBuffer.cpp @@ -71,6 +71,11 @@ FrameIndex SourceBuffer::getBackFrameIndex( const View view ) const }; } +bool SourceBuffer::isBackFrameEmpty( const View view ) const +{ + return _getQueue( view ).back().empty(); +} + void SourceBuffer::pop( const View view ) { _getQueue( view ).pop(); diff --git a/deflect/SourceBuffer.h b/deflect/SourceBuffer.h index 0862938..65b9cfd 100644 --- a/deflect/SourceBuffer.h +++ b/deflect/SourceBuffer.h @@ -66,6 +66,9 @@ class SourceBuffer /** @return the frame index of the back of the buffer for a given view. */ FrameIndex getBackFrameIndex( View view ) const; + /** @return true if the back frame of the given view has no segments. */ + bool isBackFrameEmpty( View view ) const; + /** Insert a segment into the back frame of the appropriate queue. */ void insert( const Segment& segment, const View view ); diff --git a/doc/StereoStreaming.md b/doc/StereoStreaming.md index cc7a4f0..3f05095 100644 --- a/doc/StereoStreaming.md +++ b/doc/StereoStreaming.md @@ -31,7 +31,7 @@ payload. This message is silently ignored by older Servers. ## Examples -Example of a stereo 3D client application: +Example of a stereo 3D client application using the blocking Stream API: deflect::Stream stream( ... ); @@ -43,19 +43,55 @@ Example of a stereo 3D client application: deflect::ImageWrapper leftImage( data, width, height, deflect::RGBA ); leftImage.view = deflect::View::LEFT_EYE; - deflectStream->send( leftImage ) && deflectStream->finishFrame(); + deflectStream->send( leftImage ); /** ...render right image... */ deflect::ImageWrapper rightImage( data, width, height, deflect::RGBA ); rightImage.view = deflect::View::RIGHT_EYE; - deflectStream->send( rightImage ) && deflectStream->finishFrame(); + deflectStream->send( rightImage ); + + deflectStream->finishFrame(); + + /** ...synchronize with other render clients (network barrier)... */ + } + +Example of a stereo 3D client application using the asynchronous Stream API: + + deflect::Stream stream( ... ); + + /** ...synchronize start with other render clients (network barrier)... */ + + delfect::Stream::Future leftFuture, rightFuture; + leftFuture = deflect::qt::make_ready_future( true ); + rightFuture = deflect::qt::make_ready_future( true ); + + ImageData leftData, rightData; // must remain valid until sending completes + + renderLoop() + { + if( !leftFuture.valid() || !leftFuture.get( )) + return; + + /** ...render left image... */ + + deflect::ImageWrapper leftImage( leftData, width, height, deflect::RGBA ); + leftImage.view = deflect::View::LEFT_EYE; + leftFuture = deflectStream->asyncSend( leftImage ); + + if( !rightFuture.valid() || !rightFuture.get( )) + return; + + /** ...render right image... */ + + deflect::ImageWrapper rightImage( rightData, width, height, deflect::RGBA ); + rightImage.view = deflect::View::RIGHT_EYE; + rightFuture = deflectStream->send( rightImage ); /** ...synchronize with other render clients (network barrier)... */ } -For a more complete example, please refer to the SimpleStreamer application -source code. +For a complete code example, please refer to the SimpleStreamer application. ## Issues diff --git a/tests/cpp/ReceiveBufferTests.cpp b/tests/cpp/ReceiveBufferTests.cpp index dab60fe..bf0bf35 100644 --- a/tests/cpp/ReceiveBufferTests.cpp +++ b/tests/cpp/ReceiveBufferTests.cpp @@ -322,7 +322,7 @@ void _insert( deflect::ReceiveBuffer& buffer, const size_t sourceIndex, { for( const auto& segment : frame ) buffer.insert( segment, sourceIndex, view ); - buffer.finishFrameForSource( sourceIndex, view ); + buffer.finishFrameForSource( sourceIndex ); } void _testStereoBuffer( deflect::ReceiveBuffer& buffer ) @@ -360,6 +360,29 @@ BOOST_AUTO_TEST_CASE( TestStereoOneSource ) _testStereoBuffer( buffer ); } +BOOST_AUTO_TEST_CASE( TestStereoSingleFinishFrame ) +{ + const size_t sourceIndex = 46; + + deflect::ReceiveBuffer buffer; + buffer.addSource( sourceIndex ); + + const deflect::Segments testSegments = generateTestSegments(); + + for( const auto& segment : testSegments ) + buffer.insert( segment, sourceIndex, deflect::View::LEFT_EYE ); + BOOST_CHECK( !buffer.hasCompleteStereoFrame( )); + + for( const auto& segment : testSegments ) + buffer.insert( segment, sourceIndex, deflect::View::RIGHT_EYE ); + BOOST_CHECK( !buffer.hasCompleteStereoFrame( )); + + buffer.finishFrameForSource( sourceIndex ); + BOOST_CHECK( buffer.hasCompleteStereoFrame( )); + + _testStereoBuffer( buffer ); +} + BOOST_AUTO_TEST_CASE( TestStereoTwoSourcesScreenSpaceSplit ) { const size_t sourceIndex1 = 46;