[vlmc-devel] [PATCH 05/10] Add ClipSmemRenderer
yikei lu
luyikei.qmltu at gmail.com
Thu May 19 01:03:01 CEST 2016
> I'm quite sure I had nothing to do with it :D
A lot of code in this file comes from the old VideoWorkflow :)
2016-05-19 4:32 GMT+09:00 Hugo Beauzée-Luyssen <hugo at beauzee.fr>:
> On 05/04/2016 04:18 PM, Yikai Lu wrote:
>> ---
>> src/Renderer/ClipSmemRenderer.cpp | 317 ++++++++++++++++++++++++++++++++++++++
>> src/Renderer/ClipSmemRenderer.h | 101 ++++++++++++
>> 2 files changed, 418 insertions(+)
>> create mode 100644 src/Renderer/ClipSmemRenderer.cpp
>> create mode 100644 src/Renderer/ClipSmemRenderer.h
>>
>> diff --git a/src/Renderer/ClipSmemRenderer.cpp b/src/Renderer/ClipSmemRenderer.cpp
>> new file mode 100644
>> index 0000000..5565d5b
>> --- /dev/null
>> +++ b/src/Renderer/ClipSmemRenderer.cpp
>> @@ -0,0 +1,317 @@
>> +/*****************************************************************************
>> + * ClipSmemRenderer.cpp: Render from a Clip
>> + *****************************************************************************
>> + * Copyright (C) 2008-2016 VideoLAN
>> + *
>> + * Authors: Hugo Beauzée-Luyssen <hugo at beauzee.fr>
>
> I'm quite sure I had nothing to do with it :D
>
>> + * Yikei Lu <luyikei.qmltu at gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
>> + *****************************************************************************/
>> +
>> +#include <QMutex>
>> +#include <QWaitCondition>
>> +
>> +#include "ClipSmemRenderer.h"
>> +#include "Backend/ISource.h"
>> +#include "Backend/ISourceRenderer.h"
>> +#include "Project/Project.h"
>> +#include "Tools/VlmcDebug.h"
>> +#include "Media/Clip.h"
>> +#include "Media/Media.h"
>> +#include "Main/Core.h"
>> +
>> +ClipSmemRenderer::ClipSmemRenderer( ClipHelper* ch, quint32 width, quint32 height )
>> + : m_eventWatcher( new RendererEventWatcher )
>> + , m_width( width )
>> + , m_height( height )
>> +{
>> + m_renderer = ch->clip()->media()->source()->createRenderer( m_eventWatcher );
>> + m_renderer->setName( qPrintable( QString( "ClipSmemRenderer " ) + ch->uuid().toString() ) );
>> +
>> + if ( ch->formats() & ClipHelper::Audio )
>> + {
>> + m_renderer->setOutputAudioCodec( "f32l" );
>> + m_renderer->setOutputAudioNumberChannels( 2 );
>> + m_renderer->setOutputAudioSampleRate( 48000 );
>> + }
>> + if ( ch->formats() & ClipHelper::Video )
>> + {
>> + m_renderer->setOutputWidth( m_width );
>> + m_renderer->setOutputHeight( m_height );
>> + m_renderer->setOutputFps( Core::instance()->project()->fps() );
>> + m_renderer->setOutputVideoCodec( "RV32" );
>> + }
>> +
>> + if ( ch->formats() == ( ClipHelper::Video | ClipHelper::Audio ) )
>> + m_renderer->enableOutputToMemory( this, this, &videoLock, &videoUnlock, &audioLock, &audioUnlock,/*m_fullSpeedRender*/false );
>> + else if ( ch->formats() & ClipHelper::Video )
>> + m_renderer->enableVideoOutputToMemory( this, &videoLock, &videoUnlock, /*m_fullSpeedRender*/false );
>> + else if ( ch->formats() & ClipHelper::Audio )
>> + m_renderer->enableAudioOutputToMemory( this, &audioLock, &audioUnlock, /*m_fullSpeedRender*/false );
>> +
>> + for ( int i = 0; i < Workflow::NbTrackType; i++ )
>> + {
>> + m_renderLock[i] = new QMutex;
>> + m_renderWaitCond[i] = new QWaitCondition;
>> + }
>> +
>> + connect( this, &ClipSmemRenderer::bufferReachedMax, this, &ClipSmemRenderer::pause, Qt::QueuedConnection );
>> +
>> + preallocate();
>> +}
>> +
>> +ClipSmemRenderer::~ClipSmemRenderer()
>> +{
>> + stop();
>> + delete m_eventWatcher;
>> + delete m_renderer;
>> + for ( int i = 0; i < Workflow::NbTrackType; i++ )
>> + {
>> + delete m_renderLock[i];
>> + delete m_renderWaitCond[i];
>> + }
>> + releasePrealocated();
>> +}
>> +
>> +RendererEventWatcher*
>> +ClipSmemRenderer::eventWatcher()
>> +{
>> + return m_eventWatcher;
>> +}
>> +
>> +qint32
>> +ClipSmemRenderer::maxNumBuffers( Workflow::TrackType trackType )
>> +{
>> + if ( trackType == Workflow::AudioTrack )
>> + return 256;
>> + else
>> + return 3 * 30;
>> +}
>> +
>> +Workflow::Frame*
>> +ClipSmemRenderer::getOutput( Workflow::TrackType trackType, ClipSmemRenderer::GetMode mode, qint64 currentFrame )
>> +{
>> + Q_UNUSED( currentFrame )
>> +
>> + QMutexLocker lock( m_renderLock[trackType] );
>> +
>> + if ( m_lastReturnedBuffer[trackType] != nullptr )
>> + {
>> + m_availableBuffers[trackType].enqueue( m_lastReturnedBuffer[trackType] );
>> + m_lastReturnedBuffer[trackType] = nullptr;
>> + }
>> + if ( m_computedBuffers[trackType].count() == 0 )
>> + {
>> + if ( m_renderWaitCond[trackType]->wait( m_renderLock[trackType], 100 ) == false )
>> + {
>> + vlmcWarning() << "ClipSmemRenderer: Timed out while waiting for a frame";
>> + return nullptr;
>> + }
>> + }
>> + Workflow::Frame *buff = nullptr;
>> + if ( mode == GetMode::Pop )
>> + {
>> + buff = m_computedBuffers[trackType].dequeue();
>> + m_lastReturnedBuffer[trackType] = buff;
>> + }
>> + else
>> + buff = m_computedBuffers[trackType].head();
>> +
>> + //If we're running out of computed buffers, refill our stack.
>> + if ( m_computedBuffers[trackType].count() < maxNumBuffers( trackType ) / 3 )
>> + setPause( false );
>> + //Don't test using availableBuffer, as it may evolve if a buffer is required while
>> + //no one is available : we would spawn a new buffer, thus modifying the number of available buffers
>> + else if ( m_computedBuffers[trackType].count() >= maxNumBuffers( trackType ) )
>> + {
>> + // It's OK to check from here: if getOutput is not called, it means the clipworkflow is
>> + // stopped or preloading, in which case, we don't care about the buffer queue growing uncontrolled
>> + setPause( true );
>> + }
>> +
>> + // Fail safe
>> + if ( buff->buffer() == nullptr )
>> + return nullptr;
>> +
>> + return buff;
>> +}
>> +
>> +void
>> +ClipSmemRenderer::preallocate()
>> +{
>> + for ( int i = 0; i < Workflow::NbTrackType; ++i )
>> + {
>> + QMutexLocker lock( m_renderLock[i] );
>> + while ( m_availableBuffers[i].isEmpty() == false )
>> + delete m_availableBuffers[i].dequeue();
>> + for ( int j = 0; j < maxNumBuffers( (Workflow::TrackType)i ); ++j )
>> + m_availableBuffers[i].enqueue( new Workflow::Frame( 0 ) ); // Don't allocate too much memory.
>> + m_lastReturnedBuffer[i] = nullptr;
>> + }
>> +}
>> +
>> +void
>> +ClipSmemRenderer::releasePrealocated()
>> +{
>> + for ( int i = 0; i < Workflow::NbTrackType; ++i )
>> + {
>> + QMutexLocker lock( m_renderLock[i] );
>> + while ( m_availableBuffers[i].isEmpty() == false )
>> + delete m_availableBuffers[i].dequeue();
>> + while ( m_computedBuffers[i].isEmpty() == false )
>> + delete m_computedBuffers[i].dequeue();
>> + }
>> +}
>> +
>> +void
>> +ClipSmemRenderer::flushComputedBuffers()
>> +{
>> + for ( int i = 0; i < Workflow::NbTrackType; ++i )
>> + {
>> + QMutexLocker lock( m_renderLock[i] );
>> + while ( m_computedBuffers[i].isEmpty() == false )
>> + m_availableBuffers[i].enqueue( m_computedBuffers[i].dequeue() );
>> + }
>> +}
>> +
>> +void
>> +ClipSmemRenderer::stop()
>> +{
>> + if ( m_renderer != nullptr )
>> + m_renderer->stop();
>> +}
>> +
>> +void
>> +ClipSmemRenderer::start()
>> +{
>> + if ( m_renderer != nullptr )
>> + m_renderer->start();
>> +}
>> +
>> +void
>> +ClipSmemRenderer::setPause( bool val )
>> +{
>> + if ( m_renderer != nullptr )
>> + {
>> + m_renderer->setPause( val );
>> + if ( val )
>> + vlmcWarning() << "ClipSmemRenderer was paused unexpectedly";
>> + }
>> +}
>> +
>> +void
>> +ClipSmemRenderer::setTime( int64_t time )
>> +{
>> + if ( m_renderer != nullptr )
>> + m_renderer->setTime( time );
>> +}
>> +
>> +void
>> +ClipSmemRenderer::pause()
>> +{
>> + if ( m_renderer != nullptr ) {
>> + m_renderer->setPause( true );
>> + vlmcWarning() << "ClipSmemRenderer was paused unexpectedly";
>> + }
>> +}
>> +
>> +
>> +void
>> +ClipSmemRenderer::audioLock( void *data, quint8 **pcm_buffer, size_t size )
>> +{
>> + ClipSmemRenderer* renderer = reinterpret_cast<ClipSmemRenderer*>( data );
>> +
>> + Workflow::Frame* frame = nullptr;
>> +
>> + renderer->m_renderLock[Workflow::AudioTrack]->lock();
>> + if ( renderer->m_availableBuffers[Workflow::AudioTrack].isEmpty() == true )
>> + frame = new Workflow::Frame( size );
>> + else
>> + {
>> + frame = renderer->m_availableBuffers[Workflow::AudioTrack].dequeue();
>> + if ( frame->size() < size )
>> + frame->resize( size );
>> + }
>> + renderer->m_computedBuffers[Workflow::AudioTrack].enqueue( frame );
>> + *pcm_buffer = (uint8_t*)frame->buffer();
>> +}
>> +
>> +void
>> +ClipSmemRenderer::audioUnlock( void *data, uint8_t *pcm_buffer, unsigned int channels,
>> + unsigned int rate, unsigned int nb_samples, unsigned int bits_per_sample,
>> + size_t size, int64_t pts )
>> +{
>> + Q_UNUSED( pcm_buffer )
>> + Q_UNUSED( channels )
>> + Q_UNUSED( rate )
>> + Q_UNUSED( nb_samples )
>> + Q_UNUSED( bits_per_sample )
>> + Q_UNUSED( size )
>> +
>> + ClipSmemRenderer* renderer = reinterpret_cast<ClipSmemRenderer*>( data );
>> +
>> + Workflow::Frame *frame = renderer->m_computedBuffers[Workflow::AudioTrack].last();
>> + frame->pts = pts;
>> + renderer->m_renderWaitCond[Workflow::AudioTrack]->wakeAll();
>> + if ( maxNumBuffers( Workflow::AudioTrack ) <= renderer->m_computedBuffers[Workflow::AudioTrack].count() )
>> + emit renderer->bufferReachedMax();
>> + renderer->m_renderLock[Workflow::AudioTrack]->unlock();
>> +
>> +}
>> +
>> +void
>> +ClipSmemRenderer::videoLock( void *data, uint8_t **p_buffer, size_t size )
>> +{
>> + ClipSmemRenderer* renderer = reinterpret_cast<ClipSmemRenderer*>( data );
>> +
>> + //Mind the fact that frame size in bytes might not be width * height * bpp
>> + Workflow::Frame* frame = nullptr;
>> +
>> + renderer->m_renderLock[Workflow::VideoTrack]->lock();
>> + if ( renderer->m_availableBuffers[Workflow::VideoTrack].isEmpty() == true )
>> + {
>> + frame = new Workflow::Frame( size );
>> + }
>> + else
>> + {
>> + frame = renderer->m_availableBuffers[Workflow::VideoTrack].dequeue();
>> + if ( frame->size() < size )
>> + frame->resize( size );
>> + }
>> + renderer->m_computedBuffers[Workflow::VideoTrack].enqueue( frame );
>> + *p_buffer = (uint8_t*)frame->buffer();
>> +}
>> +
>> +void
>> +ClipSmemRenderer::videoUnlock( void *data, uint8_t *buffer, int width, int height,
>> + int bpp, size_t size, int64_t pts )
>> +{
>> + Q_UNUSED( buffer )
>> + Q_UNUSED( width )
>> + Q_UNUSED( height )
>> + Q_UNUSED( bpp )
>> + Q_UNUSED( size )
>> +
>> + ClipSmemRenderer* renderer = reinterpret_cast<ClipSmemRenderer*>( data );
>> +
>> + Workflow::Frame *frame = renderer->m_computedBuffers[Workflow::VideoTrack].last();
>> + frame->pts = pts;
>> + renderer->m_renderWaitCond[Workflow::VideoTrack]->wakeAll();
>> + if ( maxNumBuffers( Workflow::VideoTrack ) <= renderer->m_computedBuffers[Workflow::VideoTrack].count() )
>> + emit renderer->bufferReachedMax();
>> + renderer->m_renderLock[Workflow::VideoTrack]->unlock();
>> +
>> +}
>> diff --git a/src/Renderer/ClipSmemRenderer.h b/src/Renderer/ClipSmemRenderer.h
>> new file mode 100644
>> index 0000000..aee4bda
>> --- /dev/null
>> +++ b/src/Renderer/ClipSmemRenderer.h
>> @@ -0,0 +1,101 @@
>> +/*****************************************************************************
>> + * ClipSmemRenderer.cpp: Render from a Clip
>> + *****************************************************************************
>> + * Copyright (C) 2008-2016 VideoLAN
>> + *
>> + * Authors: Hugo Beauzée-Luyssen <hugo at beauzee.fr>
>> + * Yikei Lu <luyikei.qmltu at gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
>> + *****************************************************************************/
>> +
>> +#ifndef CLIPSMEMRENDERER_H
>> +#define CLIPSMEMRENDERER_H
>> +
>> +class QMutex;
>> +class QWaitCondition;
>> +
>> +#include <QObject>
>> +#include <QQueue>
>> +
>> +#include "Workflow/ClipHelper.h"
>> +#include "Workflow/Types.h"
>> +#include "Tools/RendererEventWatcher.h"
>> +
>> +class ClipSmemRenderer : public QObject
>> +{
>> + Q_OBJECT
>> + Q_DISABLE_COPY( ClipSmemRenderer )
>> +
>> +public:
>> +
>> + /**
>> + * \brief Used to know which way you want to get a computed output.
>> + * Pop: the buffer is popped and returned
>> + * Get: the buffer is just returned (for paused mode for instance)
>> + */
>> + enum GetMode
>> + {
>> + Pop,
>> + Get,
>> + };
>> +
>> + explicit ClipSmemRenderer( ClipHelper* ch, quint32 width = 0, quint32 height = 0 );
>> + ~ClipSmemRenderer();
>> +
>> + Workflow::Frame* getOutput( Workflow::TrackType trackType, GetMode mode, qint64 currentFrame );
>> + void stop();
>> + void start();
>> + void setPause( bool val );
>> + void setTime( int64_t time );
>> +
>> + void releasePrealocated();
>> + void flushComputedBuffers();
>> +
>> + RendererEventWatcher* eventWatcher();
>> +
>> +private:
>> + static qint32 maxNumBuffers( Workflow::TrackType trackType );
>> +
>> + Backend::ISourceRenderer* m_renderer;
>> + RendererEventWatcher* m_eventWatcher;
>> + QMutex* m_renderLock[Workflow::NbTrackType];
>> + QWaitCondition* m_renderWaitCond[Workflow::NbTrackType];
>> +
>> + QQueue<Workflow::Frame*> m_availableBuffers[Workflow::NbTrackType];
>> + QQueue<Workflow::Frame*> m_computedBuffers[Workflow::NbTrackType];
>> + Workflow::Frame* m_lastReturnedBuffer[Workflow::NbTrackType];
>> +
>> + quint32 m_width;
>> + quint32 m_height;
>> +
>> + void preallocate();
>> + void pause();
>> +
>> + static void audioLock( void *data,quint8** pcm_buffer , size_t size );
>> + static void audioUnlock( void *data,
>> + uint8_t * pcm_buffer, unsigned int channels,
>> + unsigned int rate, unsigned int nb_samples,
>> + unsigned int bits_per_sample,
>> + size_t size, int64_t pts );
>> + static void videoLock( void* data, uint8_t** p_buffer, size_t size );
>> + static void videoUnlock( void *data, uint8_t* buffer, int width,
>> + int height, int bpp, size_t size, int64_t pts );
>> +signals:
>> + void bufferReachedMax();
>> +
>> +};
>> +
>> +#endif // CLIPSMEMRENDERER_H
>>
>
> This could use a summary of the intent for this class
>
> --
> Hugo Beauzée-Luyssen
> www.beauzee.fr
> _______________________________________________
> Vlmc-devel mailing list
> Vlmc-devel at videolan.org
> https://mailman.videolan.org/listinfo/vlmc-devel
More information about the Vlmc-devel
mailing list