/*
* Copyright (c) 2009 Ludovic Fauvet
*
* Author: Ludovic Fauvet <etix@l0cal.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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "MainWindow.h"
MainWindow::MainWindow( QWidget* parent )
: QMainWindow( parent )
{
// We do not support video resizing
setFixedSize( 500, 450 );
// This is the widget where the video will be drawn
// The advantage of a QLabel over a QWidget is that
// we can easily push a QPixmap into.
video = new QLabel( this );
video->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
playButton = new QPushButton( "Play" );
// The container will contain the main layout
// Video + Play button
QWidget* container = new QWidget( this );
setCentralWidget( container );
// Vertical Layout
layout = new QVBoxLayout;
layout->addWidget( video );
layout->addWidget( playButton );
container->setLayout( layout );
// Used for printing libvlc exceptions (if any)
errorHandler = new QErrorMessage( this );
// Connecting the play button to the play slot
connect( playButton, SIGNAL( clicked() ), this, SLOT( play() ) );
// Forget that for the moment ;-)
connect( this, SIGNAL( frameReady( struct ctx* ) ),
this, SLOT( processNewFrame( struct ctx* ) ) );
// Finally init libvlc
initVLC();
}
void MainWindow::initVLC()
{
// List of parameters used to initialize libvlc.
// These arguments are same as those you can pass
// the the VLC command line.
char const* vlc_argv[] =
{
"--verbose", "3",
// Edit this line if libvlc can't locate your plugins directory
//"--plugin-path", "/path/to/vlc",
};
int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);
// Initialize the libvlc
exception mechanism
libvlc_exception_init( &m_ex );
// Create a libvlc instance
m_vlcInstance = libvlc_new( vlc_argc, vlc_argv, &m_ex );
// This for catching and printing exceptions
// raised by libvlc
catchException();
// Create the mediaplayer used to play a media
m_vlcMediaplayer = libvlc_media_player_new( m_vlcInstance, &m_ex );
// Re-checking for exceptions
catchException();
// We're done with the initialization!
}
void MainWindow::play()
{
// This method is called, when the play button is
// clicked.
if ( mrl.isEmpty() )
{
// Show the open dialog
mrl = QFileDialog::getOpenFileName( this,
"Open a video file",
QDir::home().dirName(),
"Videos (*.avi *.ogg *.mkv "
"*.mpg *.mpeg *.wmv)" );
if ( mrl.isEmpty() ) return;
// Create a new media from the Media Resource Locator
m_vlcMedia = libvlc_media_new( m_vlcInstance, mrl.toAscii(), &m_ex );
// Catching
exception
catchException();
// We now need a struct for storing the video buffer
// and a
mutex to protect it.
// The structure will be given as an arguments for the
// lock/unlock callbacks.
struct ctx* context;
// Allocating the space for the structure
context = ( struct ctx* )malloc( sizeof( *context ) );
// Allocating the video buffer
context->pixels = ( uchar* )malloc( ( sizeof( *( context->pixels ) ) * VIDEO_WIDTH * VIDEO_HEIGHT ) * 4 );
// Allocating the
mutex
context->
mutex = new QMutex();
context->mainWindow = this;
// Creating some char[] to store the media options
char clock[64], cunlock[64], cdata[64];
char width[32], height[32], chroma[32], pitch[32];
// Preparing the options for the media
// The clock and cunlock contain a pointer to the associated
// static method (note the use of %lld).
//
// In that specific case we can't use Qt:
// The sprintf method of the QString does not support
// length modifiers (like %lld).
sprintf( clock, ":vmem-lock=%lld", (long long int)(intptr_t)lock );
sprintf( cunlock, ":vmem-unlock=%lld", (long long int)(intptr_t)unlock );
sprintf( cdata, ":vmem-data=%lld", (long long int)(intptr_t)context );
sprintf( width, ":vmem-width=%i", VIDEO_WIDTH );
sprintf( height, ":vmem-height=%i", VIDEO_HEIGHT );
sprintf( chroma, ":vmem-chroma=%s", "RV32" );
sprintf( pitch, ":vmem-pitch=%i", VIDEO_WIDTH * 4 );
// List of options
// This part can be easily replaced by a QStringList
// instead of a C array.
char const* media_options[] =
{
":vout=vmem",
width, height,
chroma, pitch,
clock, cunlock,
cdata
};
int media_options_size = sizeof( media_options )
/ sizeof( *media_options );
// Adding each option from the array to the media
for ( int i = 0; i < media_options_size; ++i )
{
libvlc_media_add_option( m_vlcMedia, media_options[i], &m_ex );
catchException();
}
// Put the media into the mediaplayer
libvlc_media_player_set_media( m_vlcMediaplayer, m_vlcMedia, &m_ex );
catchException();
}
// Finally, start the playback.
libvlc_media_player_play( m_vlcMediaplayer, &m_ex );
catchException();
}
void MainWindow::lock( struct ctx* ctx, void** pp_ret )
{
// Lock the buffer (to avoid concurrent
access and data corruption)
ctx->
mutex->lock();
// Tell libvlc to write the next frame into our pre-allocated buffer
*pp_ret = ctx->pixels;
}
void MainWindow::unlock( struct ctx* ctx )
{
// As we are in a static method we don't have an instance
// of the MainWindow here. Fortunately we created a copy
// of our instance pointer into the ctx structure,
// do you remember ?
ctx->mainWindow->emit frameReady( ctx );
// That way we are able to emit a signal containing our
// new video frame !
}
void MainWindow::processNewFrame( struct ctx* ctx )
{
// WARNING:
// This part of the code is extremely slow and inefficient
// (but sufficient for our example).
// The bottleneck starts here ;)
// Conversion of the frame from our pixel buffer to a QImage
QImage px( ctx->pixels, VIDEO_WIDTH, VIDEO_HEIGHT, QImage::Format_RGB32 );
// Then we're creating a new QPixmap from our QImage
QPixmap pix = QPixmap::fromImage( px );
// Set the current frame into the video container
video->setPixmap( pix );
// Releasing the
mutex for the upcoming frame.
ctx->
mutex->unlock();
}
bool MainWindow::catchException()
{
if ( libvlc_exception_raised( &m_ex ) )
{
/* For VLC <= 1.0.x */
errorHandler->showMessage( libvlc_exception_get_message( &m_ex ) );
/* For VLC >= 1.1.x */
//errorHandler->showMessage( libvlc_errmsg() );
libvlc_exception_clear( &m_ex );
return true;
}
return false;
}