[vlmc-devel] [PATCH 05/10] Add ClipSmemRenderer

Hugo Beauzée-Luyssen hugo at beauzee.fr
Wed May 18 21:32:10 CEST 2016


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


More information about the Vlmc-devel mailing list