Skip to content

Commit

Permalink
CR#1: allow single finishFrame after sending left + right image
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphael Dumusc committed Feb 7, 2017
1 parent eff02a4 commit 1a44d9c
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 37 deletions.
28 changes: 11 additions & 17 deletions deflect/FrameDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,38 +134,32 @@ 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;

ReceiveBuffer& buffer = _impl->streamBuffers[uri];
try
{
buffer.finishFrameForSource( sourceIndex, view );
buffer.finishFrameForSource( sourceIndex );
}
catch( const std::runtime_error& )
{
emit bufferSizeExceeded( 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 );
}
}

Expand Down
4 changes: 1 addition & 3 deletions deflect/FrameDispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
19 changes: 14 additions & 5 deletions deflect/ReceiveBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@

#include "ReceiveBuffer.h"

#include <array>
#include <cassert>
#include <set>

namespace
{
const size_t MAX_QUEUE_SIZE = 150; // stream blocked for ~5 seconds at 30Hz
const auto views = std::array<deflect::View, 3>{ deflect::View::MONO,
deflect::View::LEFT_EYE,
deflect::View::RIGHT_EYE };
}

namespace deflect
Expand Down Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions deflect/ReceiveBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion deflect/ServerWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 1 addition & 2 deletions deflect/ServerWorker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
5 changes: 5 additions & 0 deletions deflect/SourceBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions deflect/SourceBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

Expand Down
46 changes: 41 additions & 5 deletions doc/StereoStreaming.md
Original file line number Diff line number Diff line change
Expand Up @@ -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( ... );

Expand All @@ -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<bool>( true );
rightFuture = deflect::qt::make_ready_future<bool>( 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

Expand Down
25 changes: 24 additions & 1 deletion tests/cpp/ReceiveBufferTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 1a44d9c

Please sign in to comment.