Commit 8e3da8b3 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Hide VLC behind a set of interfaces.

This introduces 3 interfaces:
- IBackend: Provides the very basic functionnalies associated to a media
  backend (log hook & creating sources)
- ISource: Provides functionnalities associated to a media source. Basic
  metadata handling & spawning renderers to use this source
- IMemorySource: Describe a source which is described programmaticaly
  and fed from memory
- ISourceRenderer: Allows one to render and configure render options.

On paper, this is feature complete with regards to the old
implementation.
In practice, it is not, and pretty much all media related
fonctionnalities are broken, and will require further refactoring to
become functionnal again.
parent 0f388e31
list(APPEND VLMC_SRCS
IBackend.h
IRenderer.h
ISource.h
VLC/VLCBackend.cpp
VLC/VLCRenderer.cpp
VLC/VLCSource.cpp
VLC/LibVLCpp/VLCInstance.cpp
VLC/LibVLCpp/VLCMedia.cpp
VLC/LibVLCpp/VLCMediaPlayer.cpp
VLC/LibVLCpp/VLCpp.hpp
)
/*****************************************************************************
* IBackend.h: Provides an entry point to a backend functionnalities
*****************************************************************************
* Copyright (C) 2008-2014 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* 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 IBACKEND_H
#define IBACKEND_H
class QString;
namespace Backend
{
class ISourceRenderer;
class ISource;
class IMemorySource;
class IBackend
{
public:
virtual ~IBackend() {}
virtual ISource* createSource( const char* path ) = 0;
virtual IMemorySource* createMemorySource() = 0;
};
extern IBackend* getBackend();
}
#endif // IBACKEND_H
/*****************************************************************************
* ISource: Describes a source being rendered by a ISourceRenderer
*****************************************************************************
* Copyright (C) 2008-2014 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* 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 ISOURCE_HPP
#define ISOURCE_HPP
namespace Backend
{
class ISourceRendererEventCb;
class ISourceRenderer;
class ISource
{
public:
virtual ~ISource() {}
virtual ISourceRenderer* createRenderer( ISourceRendererEventCb* callback ) = 0;
/**
* @brief preparse Parse this source for its information.
* This method will block until computing is finished.
* @return
*/
virtual bool preparse() = 0;
virtual unsigned int width() const = 0;
virtual unsigned int height() const = 0;
virtual float fps() const = 0;
virtual bool hasVideo() const = 0;
virtual bool hasAudio() const = 0;
};
class IMemorySource
{
public:
virtual ~IMemorySource(){}
virtual void setWidth( unsigned int width ) = 0;
virtual void setHeight( unsigned int height ) = 0;
virtual void setFps( float fps ) = 0;
virtual void setAspectRatio( const char* aspectRatio ) = 0;
virtual void setNumberChannels( unsigned int nbChannels ) = 0;
virtual void setSampleRate( unsigned int sampleRate ) = 0;
virtual ISourceRenderer* createRenderer( ISourceRendererEventCb* callback ) = 0;
};
}
#endif // ISOURCE_HPP
/*****************************************************************************
* ISourceRenderer: Defines an interface to render a frame from a media source
*****************************************************************************
* Copyright (C) 2008-2014 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* 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 IRENDERER_H
#define IRENDERER_H
#include <stdint.h>
namespace Backend
{
class ISourceRendererEventCb
{
public:
virtual ~ISourceRendererEventCb() {}
virtual void onTimeChanged( int64_t ) = 0;
virtual void onPlaying() = 0;
virtual void onPaused() = 0;
virtual void onStopped() = 0;
virtual void onEndReached() = 0;
virtual void onVolumeChanged() = 0;
virtual void onPositionChanged( float ) = 0;
virtual void onLengthChanged( int64_t ) = 0;
virtual void onErrorEncountered() = 0;
};
class ISourceRenderer
{
public:
virtual ~ISourceRenderer() {}
typedef void (*VideoOutputLockCallback)( void* data, uint8_t** p_buffer, size_t size );
typedef void (*VideoOutputUnlockCallback)( void* data, uint8_t* buffer, int width,
int height, int bpp, size_t size, int64_t pts );
typedef void (*AudioOutputLockCallback)( void* data, uint8_t** p_buffer, size_t size );
typedef void (*AudioOutputUnlockCallback)( void* data, uint8_t* buffer, unsigned int channels,
unsigned int rate, unsigned int nb_samples, unsigned int bits_per_sample,
size_t size, int64_t pts );
typedef int (*MemoryInputLockCallback)( void *data, const char* cookie, int64_t *dts, int64_t *pts,
quint32 *flags, size_t *bufferSize, const void **buffer );
typedef void (*MemoryInputUnlockCallback)( void *data, const char* cookie, size_t buffSize, void *buffer );
virtual void setName( const char* name ) = 0;
/**
* @brief start Initializes and launches playback.
*/
virtual void start() = 0;
virtual void stop() = 0;
virtual void playPause() = 0;
virtual void nextFrame() = 0;
virtual void previousFrame() = 0;
virtual int volume() const = 0;
virtual void setVolume( int volume ) = 0;
/**
* @brief setOutputWidget Will direct the rendering to the specified widget.
*
* @param target The widget to render on. This parameter is platform dependent.
*/
virtual void setOutputWidget( void* target ) = 0;
// Video output
virtual void setOutputFile( const char* path ) = 0;
virtual void setOutputVideoCodec( const char* fourCC ) = 0;
virtual void setOutputWidth( unsigned int width ) = 0;
virtual void setOutputHeight( unsigned int height ) = 0;
virtual void setOutputFps( float fps ) = 0;
virtual void setOutputVideoBitrate( unsigned int vBitrate ) = 0;
// Audio output
virtual void setOutputAudioCodec( const char* fourCC ) = 0;
virtual void setOutputAudioSampleRate( unsigned int sampleRate ) = 0;
virtual void setOutputAudioNumberChannels( unsigned int nbChannels ) = 0;
virtual void setOutputAudioBitrate( unsigned int aBitrate ) = 0;
virtual int64_t time() const = 0;
virtual void setTime( int64_t time ) = 0;
virtual void setPosition( float position ) = 0;
// For video output to memory:
virtual void enableVideoOutputToMemory( void* data, VideoOutputLockCallback lock, VideoOutputUnlockCallback unlock, bool timeSync ) = 0;
// For audio output to memory:
virtual void enableAudioOutputToMemory( void* data, AudioOutputLockCallback lock, AudioOutputUnlockCallback unlock, bool timeSync ) = 0;
// For memory input:
virtual void enableMemoryInput( void* data, MemoryInputLockCallback lockCallback,
MemoryInputUnlockCallback unlockCallback ) = 0;
};
}
#endif // IRENDERER_H
/*****************************************************************************
* EventWaiter.cpp: Helper to wait on a LibVLC event
*****************************************************************************
* Copyright (C) 2008-2014 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* 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 "EventWaiter.h"
using namespace Backend::VLC;
EventWaiter::EventWaiter( LibVLCpp::MediaPlayer* mediaPlayer )
: m_mediaPlayer( mediaPlayer )
, m_validationCallback( NULL )
, m_found( true )
{
m_mediaPlayer->registerEvents( &EventWaiter::eventsCallback, this );
m_mutex.lock();
}
EventWaiter::~EventWaiter()
{
m_mediaPlayer->unregisterEvents( &EventWaiter::eventsCallback, this );
}
void EventWaiter::add(libvlc_event_type_t event)
{
m_events.push_back( event );
}
EventWaiter::Result
EventWaiter::wait(unsigned long timeoutMs)
{
if ( m_waitCond.wait( &m_mutex, timeoutMs ) == false )
{
m_mutex.unlock();
return Timeout;
}
m_mutex.unlock();
return m_found == true ? Success : Canceled;
}
void
EventWaiter::setValidationCallback(EventWaiter::ValidationCallback callback)
{
m_validationCallback = callback;
}
void EventWaiter::eventsCallback( const libvlc_event_t* event, void *data )
{
EventWaiter* self = reinterpret_cast<EventWaiter*>( data );
QMutexLocker lock( &self->m_mutex );
if ( self->m_events.contains( event->type ) == true )
{
if ( self->m_validationCallback != NULL && self->m_validationCallback( event ) == false )
return;
self->m_found = true;
self->m_waitCond.wakeAll();
return;
}
if ( event->type == libvlc_MediaPlayerEncounteredError ||
event->type == libvlc_MediaPlayerEndReached )
{
self->m_waitCond.wakeAll();
}
}
/*****************************************************************************
* EventWaiter.h: Helper to wait on a LibVLC event
*****************************************************************************
* Copyright (C) 2008-2014 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* 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 EVENTWAITER_H
#define EVENTWAITER_H
#include "VLCMediaPlayer.h"
namespace Backend
{
namespace VLC
{
class EventWaiter
{
public:
/**
* @brief initialize the EventWaiter
*
* This method will lock a mutex, preventing any further event processing.
* Basic use scheme is
* \li EventWaiter* ew = new EventWaiter( mediaPlayer );
* \li ev->add( <event> );
* \li <whatever action that will lead to an asynchronous event>
* \li we->wait(...);
*
* @warning If the event comes synchronously, this will deadlock. But if
* it happens synchronously, why would you need this at all?
*/
EventWaiter( LibVLCpp::MediaPlayer* mediaPlayer );
~EventWaiter();
enum Result
{
Success, ///The event has been emited.
Canceled,///A cancelation event has been emited first.
Timeout ///Timeout has been reached.
};
void add( libvlc_event_type_t event );
Result wait( unsigned long timeoutMs );
typedef bool (*ValidationCallback)( const libvlc_event_t* event );
void setValidationCallback( ValidationCallback callback );
private:
static void eventsCallback( const libvlc_event_t *event, void* data );
private:
LibVLCpp::MediaPlayer* m_mediaPlayer;
QList<libvlc_event_type_t> m_events;
QWaitCondition m_waitCond;
QMutex m_mutex;
ValidationCallback m_validationCallback;
bool m_found;
};
} //VLC
} //Backend
#endif // EVENTWAITER_H
/*****************************************************************************
* VLCInstance.cpp: Binding for libvlc instances
*****************************************************************************
* Copyright (C) 2008-2010 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* 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 "VLCInstance.h"
#include "vlc/vlc.h"
#include <cstdio>
#include <cassert>
using namespace LibVLCpp;
Instance::Instance(int argc, const char **argv)
{
m_internalPtr = libvlc_new( argc, argv );
assert( m_internalPtr != NULL );
}
Instance::~Instance()
{
libvlc_release( m_internalPtr );
}
void
Instance::setLogHook( void* data, libvlc_log_cb hook )
{
libvlc_log_set( m_internalPtr, hook, data );
}
......@@ -26,8 +26,6 @@
#include "VLCpp.hpp"
#include "Singleton.hpp"
#include <QObject>
struct libvlc_instance_t;
namespace LibVLCpp
......@@ -35,20 +33,12 @@ namespace LibVLCpp
/**
* \warning This class should be released after every other LibVLCpp classes.
*/
class Instance : public QObject,
public Internal< libvlc_instance_t >,
public Singleton<Instance>
class Instance : public Internal< libvlc_instance_t >
{
Q_OBJECT
private:
Instance( QObject* parent = NULL );
public:
Instance( int argc, const char** argv );
~Instance();
static void vlcLogHook( void* data, int level, const libvlc_log_t* ctx, const char* fmt, va_list args );
private:
friend class Singleton<Instance>;
void setLogHook( void *data, libvlc_log_cb hook );
};
}
......
......@@ -30,12 +30,16 @@
using namespace LibVLCpp;
Media::Media( const QString& filename ) :
Media::Media( LibVLCpp::Instance* instance, const QString& filename ) :
m_fileName( filename ),
m_tracks( NULL )
{
m_internalPtr = libvlc_media_new_location( *(LibVLCpp::Instance::getInstance()),
filename.toLocal8Bit() );
m_internalPtr = libvlc_media_new_location( *instance, filename.toLocal8Bit() );
}
Media::Media( LibVLCpp::Media &media )
{
m_internalPtr = libvlc_media_duplicate( media );
}
Media::~Media()
......@@ -51,7 +55,8 @@ Media::addOption( const char* opt )
libvlc_media_add_option_flag( m_internalPtr, opt, libvlc_media_option_trusted );
}
void Media::addOption(const QString &opt)
void
Media::addOption(const QString &opt)
{
libvlc_media_add_option_flag( m_internalPtr, opt.toLocal8Bit(), libvlc_media_option_trusted );
}
......
......@@ -31,11 +31,14 @@
namespace LibVLCpp
{
class Instance;
class Media : public Internal< libvlc_media_t >
{
public:
Media( const QString& filename );
Media( Instance* instance, const QString& filename );
Media(Media &media );
~Media();
void addOption( const char* opt );
void addOption( const QString& opt );
......
......@@ -29,166 +29,58 @@
using namespace LibVLCpp;
MediaPlayer::MediaPlayer()
: m_media( NULL )
MediaPlayer::MediaPlayer( Instance* vlcInstance )
{
m_internalPtr = libvlc_media_player_new( LibVLCpp::Instance::getInstance()->getInternalPtr() );
m_internalPtr = libvlc_media_player_new( *vlcInstance );
// Initialize the event manager
p_em = libvlc_media_player_event_manager( m_internalPtr );
registerEvents();
}
MediaPlayer::MediaPlayer( const QString& name )
: m_name( name )
, m_media( NULL )
{
m_internalPtr = libvlc_media_player_new( LibVLCpp::Instance::getInstance()->getInternalPtr() );
// Initialize the event manager
p_em = libvlc_media_player_event_manager( m_internalPtr );
registerEvents();
}
MediaPlayer::MediaPlayer( const QString& name, Media* media )
: m_name( name )
, m_media( media )
{
m_internalPtr = libvlc_media_player_new_from_media( media->getInternalPtr() );
// Initialize the event manager
p_em = libvlc_media_player_event_manager( m_internalPtr );
registerEvents();
}
#define DETACH(event) libvlc_event_detach( p_em, event, callbacks, this );
MediaPlayer::~MediaPlayer()
{
DETACH( libvlc_MediaPlayerSnapshotTaken );
DETACH( libvlc_MediaPlayerTimeChanged );
DETACH( libvlc_MediaPlayerPlaying );
DETACH( libvlc_MediaPlayerPaused );
DETACH( libvlc_MediaPlayerStopped );
DETACH( libvlc_MediaPlayerEndReached );
DETACH( libvlc_MediaPlayerPositionChanged );
DETACH( libvlc_MediaPlayerLengthChanged );
DETACH( libvlc_MediaPlayerEncounteredError );
DETACH( libvlc_MediaPlayerPausableChanged );
DETACH( libvlc_MediaPlayerSeekableChanged );
DETACH( libvlc_MediaPlayerVout );
stop();
libvlc_media_player_release( m_internalPtr );
}
#undef DETACH
#define ATTACH(event) libvlc_event_attach( p_em, event, callbacks, this );
void
MediaPlayer::registerEvents()
{
// Register the callback
ATTACH( libvlc_MediaPlayerSnapshotTaken );
ATTACH( libvlc_MediaPlayerTimeChanged );
ATTACH( libvlc_MediaPlayerPlaying );
ATTACH( libvlc_MediaPlayerPaused );
ATTACH( libvlc_MediaPlayerStopped );
ATTACH( libvlc_MediaPlayerEndReached );
ATTACH( libvlc_MediaPlayerPositionChanged );
ATTACH( libvlc_MediaPlayerLengthChanged );
ATTACH( libvlc_MediaPlayerEncounteredError );
ATTACH( libvlc_MediaPlayerPausableChanged );
ATTACH( libvlc_MediaPlayerSeekableChanged );
ATTACH( libvlc_MediaPlayerVout );
}
#undef ATTACH
/**
* Event dispatcher.
*/
void
MediaPlayer::checkForWaitedEvents(const libvlc_event_t *event)
{
QMutexLocker lock( &m_mutex );
if ( m_eventsExpected.contains( event->type ) == true )
{
// Use the user provided callback to check if this event suits him.
// This is intented to filter out some events, such as multiple length changed
// with a value of 0
if ( m_eventsCallback != NULL && m_eventsCallback( this, event ) == false )
return ;
m_eventReceived = event->type;
m_waitCond.wakeAll();
}
else if ( m_eventsCancel.contains( event->type ) == true )
{
m_eventReceived = event->type;
m_waitCond.wakeAll();
}
//Otherwise this is an event we don't care about.
MediaPlayer::registerEvents( libvlc_callback_t callback, void* data )
{
libvlc_event_attach( p_em, libvlc_MediaPlayerSnapshotTaken, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerTimeChanged, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerPlaying, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerPaused, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerStopped, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerEndReached, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerPositionChanged, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerLengthChanged, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerEncounteredError, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerPausableChanged, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerSeekableChanged, callback, data );
libvlc_event_attach( p_em, libvlc_MediaPlayerVout, callback, data );
}
void
MediaPlayer::callbacks( const libvlc_event_t* event, void* ptr )
{
Q_ASSERT_X( event->type >= libvlc_MediaPlayerMediaChanged &&
event->type < libvlc_MediaListItemAdded, "event callback", "Only libvlc_MediaPlayer* events are supported" );
MediaPlayer* self = reinterpret_cast<MediaPlayer*>( ptr );
if (event->type != libvlc_MediaPlayerPositionChanged &&
event->type != libvlc_MediaPlayerTimeChanged)
{
vlmcDebug() << self->m_name << "Event received:" << libvlc_event_type_name(event->type);
}
self->checkForWaitedEvents( event );
switch ( event->type )
{
case libvlc_MediaPlayerPlaying:
self->emit playing();
break;
case libvlc_MediaPlayerPaused:
self->emit paused();
break;
case libvlc_MediaPlayerStopped:
self->emit stopped();
break;
case libvlc_MediaPlayerEndReached:
self->emit endReached();
break;
case libvlc_MediaPlayerTimeChanged:
self->emit timeChanged( event->u.media_player_time_changed.new_time );
break;
case libvlc_MediaPlayerPositionChanged:
//vlmcDebug() << self << "position changed : " << event->u.media_player_position_changed.new_position;
self->emit positionChanged( event->u.media_player_position_changed.new_position );
break;
case libvlc_MediaPlayerLengthChanged:
self->emit lengthChanged( event->u.media_player_length_changed.new_length );
break;
case libvlc_MediaPlayerSnapshotTaken:
self->emit snapshotTaken( event->u.media_player_snapshot_taken.psz_filename );
break;
case libvlc_MediaPlayerEncounteredError:
self->emit errorEncountered();
break;
case libvlc_MediaPlayerSeekableChanged:
// TODO: Later change it to an event that corresponds volume change, when this thing gets fixed in libvlc
self->emit volumeChanged();
break;
case libvlc_MediaPlayerPausableChanged:
case libvlc_MediaPlayerTitleChanged:
case libvlc_MediaPlayerNothingSpecial:
case libvlc_MediaPlayerOpening:
case libvlc_MediaPlayerBuffering:
case libvlc_MediaPlayerForward:
case libvlc_MediaPlayerBackward:
default:
// vlmcDebug() << "Unknown mediaPlayerEvent: " << event->type;
break;
}
MediaPlayer::unregisterEvents( libvlc_callback_t callback, void *data )